mod array_index_mode;
mod index_type;
mod ste_vec_mode;

use crate::{operator::Operator, ColumnType};
pub use array_index_mode::ArrayIndexMode;
pub use index_type::IndexType;
use serde::{Deserialize, Serialize};
pub use ste_vec_mode::SteVecMode;

pub const K_DEFAULT: usize = 6;
pub const M_DEFAULT: usize = 2048;

#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum Tokenizer {
    EdgeNgram { min_gram: usize, max_gram: usize },
    Ngram { token_length: usize },
    Standard,
}

#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum TokenFilter {
    Upcase,
    Downcase,
    Stemmer,
    Stop,
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Index {
    pub version: usize,

    #[serde(flatten)]
    pub index_type: IndexType,
}

impl Index {
    pub fn new(index_type: IndexType) -> Self {
        Self {
            version: 1,
            index_type,
        }
    }

    pub fn new_match() -> Self {
        Self::new(IndexType::Match {
            token_filters: vec![TokenFilter::Downcase],
            tokenizer: Tokenizer::Ngram { token_length: 3 },
            m: M_DEFAULT,
            k: K_DEFAULT,
            include_original: true,
        })
    }

    pub fn new_ore() -> Self {
        Self::new(IndexType::Ore)
    }

    pub fn new_ope() -> Self {
        Self::new(IndexType::Ope)
    }

    pub fn new_unique() -> Self {
        Self::new(IndexType::Unique {
            token_filters: vec![],
        })
    }

    pub fn supports(&self, op: &Operator, cast_type: &ColumnType) -> bool {
        self.index_type.supports(op, cast_type)
    }

    pub fn is_orderable(&self) -> bool {
        self.index_type.is_orderable()
    }

    /// String identifier for the index type.
    /// Useful when naming columns
    pub fn as_str(&self) -> &str {
        self.index_type.as_str()
    }

    pub fn is_match(&self) -> bool {
        matches!(&self.index_type, IndexType::Match { .. })
    }

    pub fn is_ore(&self) -> bool {
        matches!(&self.index_type, IndexType::Ore)
    }

    pub fn is_ope(&self) -> bool {
        matches!(&self.index_type, IndexType::Ope)
    }

    pub fn is_unique(&self) -> bool {
        matches!(&self.index_type, IndexType::Unique { .. })
    }

    pub fn is_ste_vec(&self) -> bool {
        matches!(&self.index_type, IndexType::SteVec { .. })
    }
}
