use crate::Link;
use data::{Flow, LinksConstants};
use std::mem::transmute;

use std::ptr::NonNull;

use crate::mem::{
    header::LinksHeader,
    split::{DataPart, IndexPart},
    SplitUpdateMem,
};

use data::LinkReference;
use trees::{LinkedList, RelativeCircularLinkedList, RelativeLinkedList};

pub struct InternalSourcesLinkedList<T: LinkReference> {
    data: NonNull<[DataPart<T>]>,
    indexes: NonNull<[IndexPart<T>]>,
    r#continue: T,
    r#break: T,
}

impl<T: LinkReference> InternalSourcesLinkedList<T> {
    pub fn new(
        constants: LinksConstants<T>,
        data: NonNull<[DataPart<T>]>,
        indexes: NonNull<[IndexPart<T>]>,
    ) -> Self {
        Self {
            data,
            indexes,
            r#continue: constants.r#continue,
            r#break: constants.r#break,
        }
    }

    fn get_header(&self) -> &LinksHeader<T> {
        unsafe { transmute(&self.indexes.as_ref()[0]) }
    }

    fn get_mut_header(&mut self) -> &mut LinksHeader<T> {
        unsafe { transmute(&mut self.indexes.as_mut()[0]) }
    }

    fn get_data_part(&self, link: T) -> &DataPart<T> {
        unsafe { &self.data.as_ref()[link.as_()] }
    }

    fn get_mut_data_part(&mut self, link: T) -> &mut DataPart<T> {
        unsafe { &mut self.data.as_mut()[link.as_()] }
    }

    fn get_index_part(&self, link: T) -> &IndexPart<T> {
        unsafe { &self.indexes.as_ref()[link.as_()] }
    }

    fn get_mut_index_part(&mut self, link: T) -> &mut IndexPart<T> {
        unsafe { &mut self.indexes.as_mut()[link.as_()] }
    }

    fn get_link_value(&self, link: T) -> Link<T> {
        let data = self.get_data_part(link);
        Link::new(link, data.source, data.target)
    }

    pub fn count_usages(&self, head: T) -> T {
        self.get_size(head)
    }

    pub fn each_usages<H: FnMut(Link<T>) -> Flow + ?Sized>(
        &self,
        source: T,
        handler: &mut H,
    ) -> Flow {
        let mut current = self.get_first(source);
        let first = current;

        while current != T::from_byte(0) {
            if handler(self.get_link_value(current)).is_break() {
                return Flow::Break;
            }
            current = self.get_next(current);
            if current == first {
                return Flow::Continue;
            }
        }
        Flow::Continue
    }
}

impl<T: LinkReference> RelativeLinkedList<T> for InternalSourcesLinkedList<T> {
    fn get_first(&self, head: T) -> T {
        self.get_index_part(head).root_as_source
    }

    fn get_last(&self, head: T) -> T {
        let first = self.get_first(head);
        if first == T::from_byte(0) {
            first
        } else {
            self.get_previous(first)
        }
    }

    fn get_size(&self, head: T) -> T {
        self.get_index_part(head).size_as_source
    }

    fn set_first(&mut self, head: T, element: T) {
        self.get_mut_index_part(head).root_as_source = element;
    }

    fn set_last(&mut self, _head: T, _element: T) {
        // todo!("empty")
        // let first = self.get_index_part(head).root_as_source;
        // if first.is_zero() {
        //     self.set_first(head, element);
        // } else {
        //     self.set_previous(first, element);
        // }
    }

    fn set_size(&mut self, head: T, size: T) {
        self.get_mut_index_part(head).size_as_source = size;
    }
}

impl<T: LinkReference> LinkedList<T> for InternalSourcesLinkedList<T> {
    fn get_previous(&self, element: T) -> T {
        self.get_index_part(element).left_as_source
    }

    fn get_next(&self, element: T) -> T {
        self.get_index_part(element).right_as_source
    }

    fn set_previous(&mut self, element: T, previous: T) {
        self.get_mut_index_part(element).left_as_source = previous;
    }

    fn set_next(&mut self, element: T, next: T) {
        self.get_mut_index_part(element).right_as_source = next;
    }
}

impl<T: LinkReference> RelativeCircularLinkedList<T> for InternalSourcesLinkedList<T> {}

impl<T: LinkReference> SplitUpdateMem<T> for InternalSourcesLinkedList<T> {
    fn update_mem(&mut self, data: NonNull<[DataPart<T>]>, indexes: NonNull<[IndexPart<T>]>) {
        self.data = data;
        self.indexes = indexes;
    }
}
