use crate::index::tree::{
    IndexLimits, IndexOrd, PosRef, ToMergeCheck,
    lock_group::LockItem,
    nodes::{TreeNodeRef, block_counts_for_split},
    path::{Path, PathItem, Position},
};

#[derive(Clone)]
enum Op {
    Add(usize),
    Remove,
    Nothing,
}

#[derive(Clone)]
pub(crate) struct ChildChanged<K> {
    item: PathItem<K>,
    search_key: K,
    operation: Op,
    check_key: bool,
}
impl<K: Clone> ChildChanged<K> {
    fn new(item: PathItem<K>, search_key: K, operation: Op, check_key: bool) -> Self {
        Self {
            item,
            search_key,
            operation,
            check_key,
        }
    }

    pub(crate) fn to_check(&self, parent: PosRef<K>) -> ToMergeCheck<K> {
        ToMergeCheck::new(
            parent,
            self.item.pos_ref().clone(),
            self.check_key || self.item.position() == Position::First,
        )
    }
    pub(crate) fn search_key(&self) -> &K {
        &self.search_key
    }
    pub(crate) fn replace_item(&mut self, item: PathItem<K>) {
        self.item = item;
    }
    pub(crate) fn item(&self) -> &PathItem<K> {
        &self.item
    }
}
pub(crate) struct ParentChange<K> {
    path: Path<K>,
    children: Vec<ChildChanged<K>>,
    remove: usize,
    add: usize,
    change_prev_key: bool,
    change_next_key: bool,
}
impl<K: IndexOrd + Clone> ParentChange<K> {
    pub(crate) fn new_children(path: Path<K>, children: Vec<ChildChanged<K>>) -> Self {
        debug_assert!(!path.is_empty());
        let mut add = 0;
        let mut remove = 0;
        let mut is_same_prev_key = false;
        let mut is_same_next_key = false;
        for child in &children {
            if path.last_position() == Position::First || child.check_key {
                is_same_prev_key = true;
            }
            if child.item.position() == Position::Last {
                is_same_next_key = true;
            }
            match child.operation {
                Op::Add(counts) => add += counts,
                Op::Remove => remove += 1,
                Op::Nothing => (),
            }
        }
        Self {
            path,
            children,
            remove,
            add,
            change_prev_key: is_same_prev_key,
            change_next_key: is_same_next_key,
        }
    }
    pub(crate) fn replace(&mut self, path: Path<K>, children: Vec<ChildChanged<K>>) {
        debug_assert!(!path.is_empty());
        let mut add = 0;
        let mut remove = 0;
        let mut is_same_prev_key = false;
        let mut is_same_next_key = false;
        for child in &children {
            if child.item.position() == Position::First || child.check_key {
                is_same_prev_key = true;
            }
            if child.item.position() == Position::Last {
                is_same_next_key = true;
            }
            match child.operation {
                Op::Add(counts) => add += counts,
                Op::Remove => remove += 1,
                Op::Nothing => (),
            }
        }
        self.path = path;
        self.children = children;
        self.add = add;
        self.remove = remove;
        self.change_prev_key = is_same_prev_key;
        self.change_next_key = is_same_next_key;
    }
    pub(crate) fn new_child(child: &LockItem<K>, limits: &IndexLimits) -> Self {
        let mut path = child.path().clone();
        path.pop();
        debug_assert!(!path.is_empty());
        let mut inst = Self {
            path,
            children: Vec::new(),
            add: 0,
            remove: 0,
            change_prev_key: false,
            change_next_key: false,
        };
        inst.add_child(child, limits);
        inst
    }
    pub(crate) fn add_child(&mut self, child: &LockItem<K>, limits: &IndexLimits) {
        let check_key = if child.path().last_position() == Position::First || child.check_key() {
            self.change_prev_key = true;
            true
        } else {
            false
        };
        if child.path().last_position() == Position::Last {
            self.change_next_key = true;
        }
        let operation = if child.len() < limits.bottom() {
            self.remove += 1;
            Op::Remove
        } else if child.len() > limits.top() {
            let add = block_counts_for_split(child.len(), limits.top());
            self.add += add;
            Op::Add(add)
        } else {
            Op::Nothing
        };
        self.children.push(ChildChanged::new(
            child.path().last().cloned().unwrap(),
            child.path().search_key().clone(),
            operation,
            check_key,
        ));
    }

    pub fn to_lock(&self) -> bool {
        self.add != 0 || self.remove != 0 || self.change_prev_key || self.change_next_key
    }
    pub(crate) fn id(&self) -> TreeNodeRef {
        self.path.last_id().unwrap()
    }

    pub(crate) fn children_checks(&self) -> Vec<ToMergeCheck<K>> {
        let parent = self.path.last_pos_ref().unwrap();
        self.children
            .iter()
            .map(|i| i.to_check(parent.clone()))
            .collect::<Vec<_>>()
    }
    pub(crate) fn path(&self) -> &Path<K> {
        &self.path
    }
    pub fn add_count(&self) -> usize {
        self.add
    }
    pub fn remove_count(&self) -> usize {
        self.remove
    }
    pub fn change_prev_key(&self) -> bool {
        self.change_prev_key
    }
    pub(crate) fn children(&self) -> &Vec<ChildChanged<K>> {
        &self.children
    }
}
