//! The segments module contains a structure for independent network segments. Segments are
//! independent if none of the nodes in the segment are shared with an other segment.

use crate::actions::BetaNodeId;

use bitvec::prelude::*;
use std::cell::RefCell;

#[derive(Debug)]
pub(crate) struct Segment {
    pub(crate) betanodes: Vec<BetaNodeId>,
    pub(crate) node_linked: RefCell<BitVec>,
    pub(crate) node_modified: RefCell<BitVec>,
    pub(crate) needs_processing: RefCell<bool>,
}

impl Segment {
    pub(crate) fn new() -> Segment {
        Segment {
            betanodes: vec!(),
            node_linked: RefCell::new(bitvec!()),
            node_modified: RefCell::new(bitvec!()),
            needs_processing: RefCell::new(false),
        }
    }

    /// Push a node to the end of the segment
    pub(crate) fn push_node(&mut self, node_id: BetaNodeId, linked: bool) {
        self.betanodes.push(node_id);
        self.node_linked.borrow_mut().push(linked);
        self.node_modified.borrow_mut().push(false);
    }

    /// Does this node has active input nodes linked from outside the segment?
    /// Always true for nodes that don't link to the outside
    pub(crate) fn is_linked(&self) -> bool {
        debug_assert_eq!(self.betanodes.len(), self.node_linked.borrow().len());
        self.node_linked.borrow().all();
        true
    }

    /// Has this node received new scheduled actions since the last process iteration?
    pub(crate) fn is_modified(&self) -> bool {
        debug_assert_eq!(self.betanodes.len(), self.node_modified.borrow().len());
        self.node_modified.borrow().any()
    }

    /// Set the linked bit for the given node
    pub(crate) fn set_linked(&self, node_id: BetaNodeId, value: bool) {
        self.betanodes.iter()
            .enumerate()
            .find(|(_index, &reference)| reference == node_id)
            .map_or_else(
                || { panic!("Unknown node {:?} in {:?} in Segment", node_id, self.betanodes)},
                |(index, &_reference)| {
                    self.node_linked.borrow_mut().set(index, value);
                }
            )
    }

    /// Set the modified bit for the given node
    pub(crate) fn set_modified(&self, _node_id: BetaNodeId, _value: bool) {
        // self.betanodes.iter()
        //     .position(|(&reference)| reference == node_id)
        //     .map_or_else(
        //         || { panic!("Unknown node {:?} in {:?} in Segment", node_id, self.betanodes)},
        //         |(index)| {
        //             self.node_modified.borrow_mut().set(index, value);
        //         }
        //     )
    }

    /// Set the linked and the modified bit for the given node
    pub(crate) fn set_linked_and_modified(&self, node_id: BetaNodeId, _linked: bool, _modified: bool) {
        self.betanodes.iter()
            .enumerate()
            .find(|(_index, &reference)| reference == node_id)
            .map_or_else(
                || { panic!("Unknown node {:?} in {:?} in Segment", node_id, self.betanodes)},
                |(_index, &_reference)| {
//                    self.node_linked.borrow_mut().set(index, linked);
//                    self.node_modified.borrow_mut().set(index, modified);
                }
            )
    }

    /// Returns the index of the node in the segment array
    pub(crate) fn get_index(&self, node_id: BetaNodeId) -> usize {
        self.betanodes.iter()
            .enumerate()
            .find(|(_index, &reference)| reference == node_id)
            .map_or_else(
                || { panic!("Unknown node {:?} in {:?} in Segment", node_id, self.betanodes)},
                |(index, &_reference)| index
            )
    }

    // segments can't activate or deactivate if
    //  they have at least one node without the capability to make matches
    //  they have no pending changes
    /// Returns true if this segment can create new matches
    pub(crate) fn calculate_needs_processing(&self) -> bool {
        // bits are on for each node that is capable of producing a result
        let bits = self.node_modified.borrow().clone() | self.node_linked.borrow().clone();
        // and we need at least one action (modified)
        self.is_modified() & bits.all()
    }
}