use std::ops::{Div, Sub};
use borsh::BorshDeserialize;
use solana_program::pubkey::Pubkey;
use tabled::Tabled;
use crate::contract::state::tick::Tick;
use crate::error::ErrorCode;
use crate::program::PROGRAM_ID;
#[derive(BorshDeserialize)]
pub struct TickArrayV2 {
pub array_index: u16,
pub tick_spacing: u16,
pub clmmpool: Pubkey,
}
#[derive(Debug, Tabled)]
pub struct TickArrayToShow {
pub array_index: u16,
pub tick_spacing: u16,
pub clmmpool: Pubkey,
pub array_space: u32,
pub start_tick_index: i32,
pub end_tick_index: i32,
}
pub struct TickArray {
pub array_index: u16,
pub tick_spacing: u16,
pub clmmpool: Pubkey,
pub ticks: [Tick],
}
impl TickArray {
pub const CAP: usize = 64;
pub const LEN: usize = 2 + 2 + 32 + Tick::LEN * TickArray::CAP;
#[inline]
pub fn array_spacing(&self) -> usize {
self.tick_spacing as usize * TickArray::CAP
}
#[inline]
pub fn start_tick_index(&self) -> i32 {
Tick::min(self.tick_spacing) + (self.array_index as usize * self.array_spacing()) as i32
}
#[inline]
pub fn end_tick_index(&self) -> i32 {
self.start_tick_index() + self.array_spacing() as i32 - self.tick_spacing as i32
}
#[inline]
pub fn is_in_array(&self, tick_index: i32) -> bool {
tick_index >= self.start_tick_index() && tick_index <= self.end_tick_index()
}
#[inline]
pub fn array_index(tick_index: i32, tick_spacing: u16) -> Result<u16, ErrorCode> {
let min = Tick::min(tick_spacing);
let max = Tick::max(tick_spacing);
if tick_index < min || tick_index > max {
return Err(ErrorCode::InvalidTickIndex);
}
let array_spacing = (TickArray::CAP * tick_spacing as usize) as i32;
Ok(((tick_index - min) / array_spacing) as u16)
}
pub fn is_min_tick_array(&self) -> bool {
self.start_tick_index() == Tick::min(self.tick_spacing)
}
pub fn is_max_tick_array(&self) -> bool {
self.end_tick_index() >= Tick::max(self.tick_spacing)
}
#[inline]
pub fn tick_offset(&self, tick_index: i32) -> usize {
tick_index
.sub(self.start_tick_index())
.div(self.tick_spacing as i32) as usize
}
pub fn get_tick(&self, tick_index: i32) -> Option<&Tick> {
let offset = self.tick_offset(tick_index);
if offset < TickArray::CAP as usize {
let t = self.ticks[offset as usize];
if t.is_initialized {
return Some(&self.ticks[offset as usize]);
}
}
return None;
}
pub fn search_range(&self, tick_index: i32, a_to_b: bool) -> Option<(usize, usize)> {
match a_to_b {
true => {
if tick_index < self.start_tick_index() {
return None;
}
let end = if tick_index >= self.end_tick_index() {
TickArray::CAP - 1
} else {
self.tick_offset(tick_index)
};
Some((0, end))
}
false => {
if tick_index >= self.end_tick_index() {
return None;
}
let start = if tick_index < self.start_tick_index() {
0
} else {
self.tick_offset(tick_index) + 1
};
Some((start, TickArray::CAP - 1))
}
}
}
pub fn get_next_initialized_tick(&self, tick_index: i32, a_to_b: bool) -> Option<&Tick> {
let search_range = self.search_range(tick_index, a_to_b);
if search_range.is_none() {
return None;
}
let (start, end) = search_range.unwrap();
match a_to_b {
true => {
for i in (start..=end).rev() {
let t = self.ticks[i];
if t.is_initialized {
return Some(&self.ticks[i]);
}
}
None
}
false => {
for i in start..=end {
let t = self.ticks[i];
if t.is_initialized {
return Some(&self.ticks[i]);
}
}
None
}
}
}
pub fn get_tick_array_address(clmmpool: &Pubkey, array_index: u16) -> Pubkey {
let (address, _) = Pubkey::find_program_address(
&[
b"tick_array",
clmmpool.as_ref(),
array_index.to_le_bytes().as_ref(),
],
&PROGRAM_ID,
);
address
}
pub fn is_tick_array_valid(&self) -> bool {
for tick in &self.ticks {
if tick.is_initialized {
return true;
}
}
false
}
}