use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::{HashSet, VecDeque};
use std::fmt::{Debug, Error, Formatter};
use std::hash::{Hash, Hasher};
use std::rc::Rc;

use generational_indextree::Arena as TreeArena;

use phreak_facts::FactCore;

use crate::actions::{BetaNodeId, ScheduledAction};
use crate::betanode::BetaNode;
use crate::segment::Segment;
use crate::traits::ScheduledExecution;

/// An AlphaNode node holds facts
///
/// One AlphaNode exists for every combination of classname and triple of constants that is used in
/// any Condition, so every fact is implicitly connected to a combination of four symbols. This is
/// done during the building of the network.
#[derive(Debug)]
pub(crate) struct AlphaNode {
    pub(crate) children: VecDeque<BetaNodeId>,
    pub(crate) cell: RefCell<AlphaNodeCell>,
}

impl ScheduledExecution for AlphaNode {
    fn schedule_action(&self, priority: usize, action: ScheduledAction) {
        let scheduled_actions = &mut self.cell.borrow_mut().scheduled_actions;
        // scheduled actions is an ordered listed on priority
        // we assume that any new insertion will be near the end of the list, so we don't use
        // the standard iterator methods which start from the front
        if scheduled_actions.len() > 0 {
            let mut cursor = scheduled_actions.len() - 1;
            while cursor > 0 {
                if scheduled_actions[cursor].0 <= priority {
                    break;
                }
                cursor -= 1;
            }
            //cursor points to the position just before where we need to insert
            scheduled_actions.insert(cursor + 1, (priority, action));
        } else {
            scheduled_actions.push((priority, action))
        }
    }
}

/// Mutable data for an AlphaNode
pub(crate) struct AlphaNodeCell {
    /// Pending actions on this node
    pub(crate) scheduled_actions: Vec<(usize, ScheduledAction)>,
    /// Memory of facts that match the condition for this AlphaNode
    pub(crate) items: HashSet<Item>,
}

/// Wrapper struct around Rc<FactCore> to allow hashing etc.
pub(crate) struct Item {
    pub(crate) fact: Rc<FactCore>
}

impl Item {
    fn new(data: Rc<FactCore>) -> Self { Item { fact: data } }

    fn borrow(&self) -> &FactCore {
        self.fact.borrow()
    }
}

impl Hash for Item {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let data: &FactCore = &*self.fact.borrow();
        data.0.as_bytes().hash(state);
        data.1.as_bytes().hash(state);
        data.2.as_bytes().hash(state);
    }
}

impl PartialEq for Item {
    fn eq(&self, other: &Self) -> bool {
        let left: &FactCore = &*self.fact.borrow();
        let right: &FactCore = &*other.fact.borrow();

        left.0.as_bytes() == right.0.as_bytes() &&
            left.1.as_bytes() == right.1.as_bytes() &&
            left.2.as_bytes() == right.2.as_bytes()
    }
}

impl Eq for Item {}

impl Debug for Item {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        let data: &FactCore = &*self.fact.borrow();
        write!(f, "Item{{ {}, {}, {} }}", data.0.to_string(), data.1.to_string(), data.2.to_string())
    }
}

impl AlphaNode {
    pub(crate) fn new() -> AlphaNode {
        AlphaNode {
            children: VecDeque::new(),
            cell: RefCell::new(AlphaNodeCell {
                scheduled_actions: Vec::new(),
                items: HashSet::new(),
            }),
        }
    }

    pub(crate) fn add_fact(&self, priority: usize, fact: &Rc<FactCore>) {
        self.schedule_action(priority, ScheduledAction::FactInserted(fact.clone()));
    }

    pub(crate) fn remove_fact(&self, priority: usize, fact: &Rc<FactCore>) {
        self.schedule_action(priority, ScheduledAction::FactRemoved(fact.clone()));
    }

    /// Process the pending inserts and deletes for this node
    pub(crate) fn process_pending(&self, betanodes: &TreeArena<BetaNode>, segments: &TreeArena<Segment>) {
        let cell = &mut *self.cell.borrow_mut();
        let mut from_empty_to_nonempty = false;
        let mut from_nonempty_to_empty = false;

        for (priority, action) in cell.scheduled_actions.iter() {
            match action {
                ScheduledAction::FactInserted(fact) => {
                    if cell.items.is_empty() { from_empty_to_nonempty = true; }
                    cell.items.insert(Item::new(fact.clone()));
                }
                ScheduledAction::FactRemoved(fact) => {
                    let item = Item::new(fact.clone());
                    cell.items.remove(&item);
                    println!("deleted");
                    if cell.items.is_empty() {
                        from_nonempty_to_empty = true;
                    }
                }
                _ => { unimplemented!() }
            }
        }

        for action in cell.scheduled_actions.iter() {
            println!("Process {:?}", action);
            for child in self.children.iter() {
                let node = betanodes.get(*child).unwrap().get();

                let segment = segments[node.get_segment()].get();
                if from_empty_to_nonempty {
                    segment.set_linked_and_modified(*child, true, true);
                } else if from_nonempty_to_empty {
                    segment.set_linked_and_modified(*child, false, true);
                } else {
                    // don't touch linked status
                    segment.set_modified(*child, true);
                }


                node.schedule_action(action.0, (action.1).clone());
            }
        }
        cell.scheduled_actions.clear();
    }
}

impl Debug for AlphaNodeCell {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
        write!(f, "AlphaNodeCell{{ {:?}, size_of_items: {} }}", self.scheduled_actions, self.items.len())
    }
}
