use std::cell::{RefCell, RefMut};
use std::collections::VecDeque;
use std::rc::Rc;

use generational_arena::Arena;
use generational_indextree::{Arena as TreeArena, Node};

use phreak_facts::FactCore;

use crate::actions::{BetaNodeId, ScheduledAction, SegmentId, TokenId};
use crate::betanode::{BetaNode, recursive_delete_tokens, insert_scheduled_action};
use crate::nodes::alphanode::AlphaNode;
use crate::segment::Segment;
use crate::token::*;
use crate::traits::*;

#[derive(Debug)]
pub(crate) struct MemoryNodeData {
    pub(crate)  segment: SegmentId,
    pub(crate)  cell: RefCell<MemoryNodeCell>,
}

impl ScheduledExecution for MemoryNodeData {
    fn schedule_action(&self, priority: usize, action: ScheduledAction) {
        self.cell.borrow_mut().schedule_action(priority, action);
    }
}

#[derive(Debug)]
pub(crate) struct MemoryNodeCell {
    pub(crate) scheduled_actions: Vec<(usize, ScheduledAction)>,
    pub(crate) tokens: VecDeque<TokenId>, // can we turn this into a hash for lookup somehow, preserving the order??
}

impl Default for MemoryNodeCell {
    fn default() -> Self {
        MemoryNodeCell {
            scheduled_actions: vec![],
            tokens: VecDeque::new(),
        }
    }
}

impl ScheduledExecutionCell for MemoryNodeCell {
    fn schedule_action(&mut self, priority: usize, action: ScheduledAction) {
        insert_scheduled_action(priority, action, &mut self.scheduled_actions);
    }

    fn clear_scheduled_actions(&mut self) {
        self.scheduled_actions.clear();
    }

    fn get_fields_mut(&mut self) -> (&[(usize, ScheduledAction)], &mut VecDeque<TokenId>) {
        (&self.scheduled_actions, &mut self.tokens)
    }
}

impl Actionable<MemoryNodeCell> for MemoryNodeData {
    fn print_debug(&self, action: &ScheduledAction, self_id: BetaNodeId, betanodes: &TreeArena<BetaNode>) {
        println!("BetaNode::MemoryNode {:?}", action);
        println!("BetaNode::MemoryNode {:?}", self_id);
        println!("BetaNode::MemoryNode {:?}", betanodes[self_id]);
    }

    fn get_cell(&self) -> RefMut<MemoryNodeCell> {
        self.cell.borrow_mut()
    }

    fn fact_inserted(&self,
                     _priority: usize,
                     _rc: &Rc<FactCore>,
                     _self_id: BetaNodeId,
                     _celltokens: &mut VecDeque<TokenId>,
                     _betanodes: &TreeArena<BetaNode>,
                     _segments: &TreeArena<Segment>,
                     _tokens: &mut TreeArena<Token>,
                     _pending: &[(usize, ScheduledAction)],
                     _nil_fact: &Rc<FactCore>,
    ) {
        unreachable!("memorynode has no linked alphamemory")
    }

    fn fact_removed(&self,
                    _priority: usize,
                    _rc: &Rc<FactCore>,
                    _self_id: BetaNodeId,
                    _celltokens: &mut VecDeque<TokenId>,
                    _betanodes: &TreeArena<BetaNode>,
                    _tokens: &mut TreeArena<Token>,
                    _segments: &TreeArena<Segment>,
                    _pending: &[(usize, ScheduledAction)],
                    _nil_fact: &Rc<FactCore>,
    ) {
        unreachable!("memory node has no linked alphamemory")
    }

    fn parent_token_inserted(&self,
                             _priority: usize,
                             _token_id: TokenId,
                             _self_id: BetaNodeId,
                             _celltokens: &mut VecDeque<TokenId>,
                             _betanodes: &TreeArena<BetaNode>,
                             _segments: &TreeArena<Segment>,
                             _tokens: &mut TreeArena<Token>,
                             _alphanodes: &Arena<AlphaNode>,
                             _pending: &[(usize, ScheduledAction)],
                             _nil_fact: &Rc<FactCore>,
    ) {
        unreachable!("join node has no token storage")
    }

    fn parent_token_removed(&self,
                            _priority: usize,
                            _token_id: TokenId,
                            _self_id: BetaNodeId,
                            _celltokens: &mut VecDeque<TokenId>,
                            _betanodes: &TreeArena<BetaNode>,
                            _segments: &TreeArena<Segment>,
                            _tokens: &mut TreeArena<Token>,
                            _pending: &[(usize, ScheduledAction)],
                            _nil_fact: &Rc<FactCore>,
    ) {
        unreachable!()
    }

    fn insert_token(&self,
                    priority: usize,
                    token_id: TokenId,
                    self_id: BetaNodeId,
                    celltokens: &mut VecDeque<TokenId>,
                    betanodes: &TreeArena<BetaNode>,
                    segments: &TreeArena<Segment>,
                    tokens: &mut TreeArena<Token>,
                    pending: &[(usize, ScheduledAction)],
                    _nil_fact: &Rc<FactCore>,
    ) {
        celltokens.push_front(token_id);
        debug_assert_eq!(tokens[token_id].get().node, self_id);

        for child in self_id.children(betanodes) {
            let bn = betanodes.get(child).unwrap().get();
            bn.schedule_action(priority, ScheduledAction::ParentTokenInserted(token_id));
            segments[bn.get_segment()].get().set_modified(child, true);
        }
    }

    fn delete_token_chain(&self,
                          priority: usize,
                          parent_id: TokenId,
                          sub_id: TokenId,
                          celltokens: &mut VecDeque<TokenId>,
                          betanodes: &TreeArena<BetaNode>,
                          tokens: &mut TreeArena<Token>,
                          pending: &[(usize, ScheduledAction)],
                          _nil_fact: &Rc<FactCore>,
    ) {
        // unlink from current node
        let pos = celltokens.iter()
            .position(|element| *element == sub_id)
            .expect("Token was expected in this list");
        celltokens.remove(pos);

        if tokens[sub_id].first_child().is_none() {
            recursive_delete_tokens(parent_id, sub_id, tokens);
        } else {
            println!("propagate delete_token_chain");
            for child_id in sub_id.children(tokens) {
                let child_token = tokens.get(child_id).unwrap().get();
                let bn = betanodes.get(child_token.node).unwrap().get();
                bn.schedule_action(priority, ScheduledAction::DeleteTokenChain(parent_id, child_id));
            }
        }
    }

    fn delete_child_tokens(&self,
                           _priority: usize,
                           _token_id: TokenId,
                           _self_id: BetaNodeId,
                           _celltokens: &mut VecDeque<TokenId>,
                           _tokens: &mut TreeArena<Token>,
                           _betanodes: &TreeArena<BetaNode>,
                           _pending: &[(usize, ScheduledAction)],
                           _nil_fact: &Rc<FactCore>,
    ) {
        unimplemented!("ncc only message")
    }

    fn init_new_node(&self,
                     _self_id: BetaNodeId,
                     _node: &Node<BetaNode>,
                     _tokens: &mut TreeArena<Token>,
                     _betanodes: &TreeArena<BetaNode>,
                     _alphanodes: &Arena<AlphaNode>,
                     _nil_fact: &Rc<FactCore>,
    ) {}
}
