use crate::index::tree::{IndexOrd, PosRef, TreeNodeRef};
use std::fmt::Display;

/// Depth computed top to bottom, so the root s 0 and leaf == depth
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) struct TopDepth(pub usize);

impl std::ops::Sub for TopDepth {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        TopDepth(self.0 - rhs.0)
    }
}

/// Depth computed bottom top , so the leaf 0 and root == depth
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) struct BottomDepth(pub usize);
impl std::ops::Add for BottomDepth {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        BottomDepth(self.0 + rhs.0)
    }
}

impl std::ops::AddAssign for BottomDepth {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum Position {
    Last,
    First,
    Middle,
}
#[derive(Clone)]
pub(crate) struct PathItem<K> {
    pos: PosRef<K>,
    version: u16,
    position: Position,
    bottom_depth: Option<BottomDepth>,
}

impl<K: Display> Display for Path<K> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let path_str = self
            .path
            .iter()
            .map(|e| format!("{}", e.pos))
            .collect::<Vec<_>>()
            .join("/");
        write!(f, "/{}/", path_str,)
    }
}

impl<K> PathItem<K> {
    pub(crate) fn new(pos: PosRef<K>, version: u16, node_len: usize) -> Self {
        let position = if pos.pos == 0 {
            Position::First
        } else if pos.pos + 1 == node_len {
            Position::Last
        } else {
            Position::Middle
        };

        Self {
            pos,
            version,
            position,
            bottom_depth: None,
        }
    }
    pub(crate) fn set_depth(&mut self, depth: BottomDepth) {
        self.bottom_depth = Some(depth);
    }

    pub(crate) fn version(&self) -> u16 {
        self.version
    }
    pub(crate) fn pos_ref(&self) -> &PosRef<K> {
        &self.pos
    }

    pub(crate) fn bottom_depth(&self) -> BottomDepth {
        self.bottom_depth.unwrap()
    }
    pub(crate) fn position(&self) -> Position {
        self.position
    }
}

#[derive(Clone)]
pub(crate) struct Path<K> {
    search_key: K,
    path: Vec<PathItem<K>>,
}
impl<K: IndexOrd + Clone> Path<K> {
    pub(crate) fn new(search_key: K) -> Self {
        Self {
            search_key,
            path: Vec::new(),
        }
    }

    pub(crate) fn finish(&mut self, p: PosRef<K>, version: u16, node_len: usize, bottom_depth: BottomDepth) {
        self.replace_search_key(p.k.clone());
        self.path.push(PathItem::new(p, version, node_len));
        self.compute_depths_start(bottom_depth);
    }

    pub(crate) fn push(&mut self, p: PosRef<K>, version: u16, node_len: usize) {
        self.path.push(PathItem::new(p, version, node_len))
    }

    pub(crate) fn pop(&mut self) -> Option<PathItem<K>> {
        self.path.pop()
    }
    pub(crate) fn clear(&mut self) {
        self.path.clear();
    }
    pub(crate) fn len(&self) -> usize {
        self.path.len()
    }

    pub(crate) fn get(&self, from: TopDepth) -> Option<&PathItem<K>> {
        self.path.get(from.0)
    }

    pub(crate) fn compute_depths(&mut self) {
        self.compute_depths_start(BottomDepth(0))
    }

    pub(crate) fn compute_depths_start(&mut self, mut depth: BottomDepth) {
        for p in self.path.iter_mut().rev() {
            p.set_depth(depth);
            depth += BottomDepth(1);
        }
    }

    pub(crate) fn point_same_node(&self, other: &Self) -> bool {
        self.last_id() == other.last_id()
    }

    pub(crate) fn last_bottom_depth(&self) -> Option<BottomDepth> {
        self.last().and_then(|e| e.bottom_depth)
    }

    pub(crate) fn last_pos_ref(&self) -> Option<&PosRef<K>> {
        self.last().map(|e| &e.pos)
    }

    pub(crate) fn last(&self) -> Option<&PathItem<K>> {
        self.path.last()
    }

    pub(crate) fn last_id(&self) -> Option<TreeNodeRef> {
        self.last().map(|e| e.pos.node_ref)
    }

    pub(crate) fn last_position(&self) -> Position {
        self.last().map(|e| e.position()).unwrap()
    }

    pub(crate) fn short_to_depth(&mut self, bottom_depth: BottomDepth) {
        while self.last().and_then(|m| m.bottom_depth) != Some(bottom_depth) {
            self.path.pop();
        }
    }

    pub(crate) fn is_empty(&self) -> bool {
        self.path.is_empty()
    }

    pub(crate) fn depth(&self) -> TopDepth {
        TopDepth(self.path.len() - 1)
    }
    pub(crate) fn search_key(&self) -> &K {
        &self.search_key
    }

    pub(crate) fn replace_search_key(&mut self, search_key: K) {
        self.search_key = search_key;
    }
}
