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, insert_scheduled_action, recursive_delete_tokens};
use crate::nodes::alphanode::AlphaNode;
use crate::segment::Segment;
use crate::token::*;
use crate::traits::*;

#[derive(Debug)]
pub(crate) struct NccNodeData {
    pub(crate) segment: SegmentId,
    pub(crate) ncp: BetaNodeId,
    pub(crate) cell: RefCell<NccNodeCell>,
}

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

/// Mutable part of the betanode
#[derive(Debug)]
pub(crate) struct NccNodeCell {
    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 NccNodeCell {
    fn default() -> Self {
        NccNodeCell {
            scheduled_actions: vec![],
            tokens: VecDeque::new(),
        }
    }
}

impl ScheduledExecutionCell for NccNodeCell {
    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<NccNodeCell> for NccNodeData {
    fn print_debug(&self,
                   action: &ScheduledAction,
                   self_id: BetaNodeId,
                   betanodes: &TreeArena<BetaNode>,
    ) {
        println!("BetaNode::NccNode {:?}", action);
        println!("BetaNode::NccNode {:?}", self_id);
        println!("BetaNode::NccNode {:?}", betanodes[self_id]);
    }

    fn get_cell(&self) -> RefMut<NccNodeCell> {
        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!("No linked alphanode") }

    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!("No linked alphanode") }

    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>,
    ) {
        let new_token = tokens.new_node(
            Token::new(nil_fact.clone(), self_id));
        token_id.prepend(new_token, tokens);
        celltokens.push_front(new_token);

        if let BetaNode::NcpNode(ncp) = betanodes[self.ncp].get() {
            let mut pcellref = ncp.cell.borrow_mut();
            let mut tmp = VecDeque::new();
            while let Some(result) = pcellref.tokens.pop_front() {
                // if result belongs to token_id
                if tokens[result].get().owner == Some(token_id) {
                    println!("For this token: {:?} {:?} {:?}", new_token, result, tokens[result].get().owner);
                    tokens[new_token].get_mut().nccnode_results.insert(result);
                    tokens[result].get_mut().owner = Some(new_token);
                    println!("Add to ncc_results: {:?} {:?}", new_token, tokens[new_token].get().nccnode_results);
                } else {
                    println!("Not for this token: {:?} {:?} {:?}", new_token, result, tokens[result].get().owner);
                    tmp.push_back(result);
                }
            }
            pcellref.tokens = tmp;
        }

        if tokens[new_token].get().nccnode_results.is_empty() {
            for child in self_id.children(betanodes) {
                println!("schedule for child {:?}", child);
                betanodes[child].get()
                    .schedule_action(priority, ScheduledAction::ParentTokenInserted(new_token));
            }
        }
    }

    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>,
    ) {
        println!("{}", print_token(token_id, tokens));

        debug_assert!(celltokens.contains(&token_id));
        celltokens.remove(celltokens.iter().position(|x| *x == token_id).unwrap());

        // delete token
        token_id.remove(tokens);
    }

    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>,
    ) {
        unimplemented!()
    }

    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>,
    ) {
        println!("{}", print_token(sub_id, tokens));

        debug_assert!(celltokens.contains(&sub_id));
        celltokens.remove(celltokens.iter().position(|x| *x == sub_id).unwrap());

        if tokens[sub_id].first_child().is_none() {
            recursive_delete_tokens(parent_id, sub_id, tokens);
        } else {
            for child_id in sub_id.children(tokens) {
                let node_id = tokens[child_id].get().node;
                let bn: &BetaNode = betanodes[node_id].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>,
    ) {
        println!("nccnode_delete_child_tokens {:?} {}", token_id, print_token(token_id, tokens));
        debug_assert!(celltokens.contains(&token_id));

        let mut to_delete = vec!();
        for child_token_id in token_id.children(&tokens) {
            to_delete.push(child_token_id);
        }

        for current_token_id in to_delete {
            println!("delete token {:?} and children", current_token_id);
            current_token_id.detach(tokens);
            let bn: &BetaNode = betanodes[tokens[current_token_id].get().node].get();
            bn.schedule_action(priority, ScheduledAction::DeleteTokenChain(current_token_id, current_token_id));
        }
    }

    fn activate_token(&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>,
    ) {
        if celltokens.contains(&token_id) {
            debug_assert!(tokens[token_id].get().nccnode_results.is_empty());
            for child_id in self_id.children(&betanodes) {
                let bn: &BetaNode = betanodes[child_id].get();
                bn.schedule_action(priority, ScheduledAction::ParentTokenInserted(token_id));
            }
        } // else this token has been removed and is about to be deleted
    }

    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>,
    ) {
        let priority = 0;

        let parent_id = node.parent().unwrap();
        match betanodes.get(parent_id).unwrap().get() {
            BetaNode::RootNode(d) => {
                self.schedule_action(priority, ScheduledAction::ParentTokenInserted(d.root_token.unwrap()));
            }
            BetaNode::MemoryNode(d) => {
                for token_id in d.cell.borrow().tokens.iter() {
                    self.schedule_action(priority, ScheduledAction::ParentTokenInserted(*token_id));
                }
            }
            BetaNode::NegativeNode(d) => {
                for token_id in d.cell.borrow().tokens.iter() {
                    self.schedule_action(priority, ScheduledAction::ParentTokenInserted(*token_id));
                }
            }
            BetaNode::NccNode(d) => {
                for token_id in d.cell.borrow().tokens.iter() {
                    if tokens[*token_id].get().nccnode_results.is_empty() {
                        self.schedule_action(priority, ScheduledAction::ParentTokenInserted(*token_id));
                    }
                }
            }
            _ => { unreachable!() }
        }
    }
}