use crate::{path_map::PathMap, text_content::TextContent, vector::Vector};

use pns::{Pid, Tid};
use text_editing::TextLine;

use std::collections::BTreeMap;

pub struct TransitionNodeMap {
    pub nodes: BTreeMap<Tid, TransitionNode>,
    pub paths: PathMap<String, Tid>,
}

impl TransitionNodeMap {
    pub fn new() -> Self {
        Self {
            nodes: BTreeMap::new(),
            paths: PathMap::new(),
        }
    }

    pub fn from_map(nodes: BTreeMap<Tid, TransitionNode>) -> Self {
        let mut paths = PathMap::new();
        for (&id, node) in &nodes {
            let path = node.path.clone();
            paths.insert(path, id);
        }
        Self { nodes, paths }
    }

    pub fn node(&self, tid: Tid) -> &TransitionNode {
        &self.nodes[&tid]
    }

    pub fn node_mut(&mut self, tid: Tid) -> &mut TransitionNode {
        self.nodes.get_mut(&tid).unwrap()
    }

    pub fn insert(&mut self, tid: Tid, node: TransitionNode) {
        let path = node.path.clone();
        self.nodes.insert(tid, node);
        self.paths.insert(path, tid);
    }

    pub fn remove(&mut self, tid: Tid) -> Option<TransitionNode> {
        if let Some(node) = self.nodes.remove(&tid) {
            self.paths.remove(&node.path, &tid);
            Some(node)
        } else {
            None
        }
    }
}

pub struct PlaceNode {
    pub positions: Vec<Vector>,
    pub name: TextLine,
    pub name_cursor: usize,
}

impl PlaceNode {
    pub fn new(positions: &[Vector], name: String) -> Self {
        let name = TextLine::from_string(name.chars().collect());
        let name_cursor = name.len();
        Self {
            positions: positions.iter().cloned().collect::<Vec<_>>(),
            name,
            name_cursor,
        }
    }
}

pub struct TransitionNode {
    pub position: Vector,
    pub path: Vec<String>,
    pub name: TextLine,
    pub name_cursor: usize,
    pub content: TextContent,
}

impl TransitionNode {
    pub fn new(position: Vector, path: Vec<String>, name: String) -> Self {
        let name = TextLine::from_string(name.chars().collect());
        let name_cursor = name.len();
        Self {
            position,
            path,
            name,
            name_cursor,
            content: TextContent::new(vec![TextLine::new()]),
        }
    }
}

pub struct NodeContainer {
    places: BTreeMap<Pid, PlaceNode>,
    pub transitions: TransitionNodeMap,
}

impl NodeContainer {
    pub fn empty() -> Self {
        Self {
            places: BTreeMap::new(),
            transitions: TransitionNodeMap::new(),
        }
    }

    pub fn new(
        places: BTreeMap<Pid, PlaceNode>,
        transitions: BTreeMap<Tid, TransitionNode>,
    ) -> Self {
        Self {
            places,
            transitions: TransitionNodeMap::from_map(transitions),
        }
    }

    pub fn place(&self, pid: Pid) -> &PlaceNode {
        &self.places[&pid]
    }

    pub fn place_mut(&mut self, pid: Pid) -> &mut PlaceNode {
        self.places.get_mut(&pid).unwrap()
    }

    pub fn get_place(&self, pid: Pid) -> Option<&PlaceNode> {
        self.places.get(&pid)
    }

    pub fn add_place(&mut self, pid: Pid, node: PlaceNode) {
        self.places.insert(pid, node);
    }

    pub fn remove_place(&mut self, pid: Pid) {
        self.places.remove(&pid);
    }

    pub fn iter_places(
        &self,
    ) -> impl Iterator<Item = (&Pid, &PlaceNode)> + DoubleEndedIterator + ExactSizeIterator {
        self.places.iter()
    }

    pub fn iter_places_mut(
        &mut self,
    ) -> impl Iterator<Item = (&Pid, &mut PlaceNode)> + DoubleEndedIterator + ExactSizeIterator
    {
        self.places.iter_mut()
    }
}
