use pns::{Pid, Tid};

use std::{
    collections::{BTreeSet as Set, HashMap as Map},
    hash::Hash,
};

pub enum NodeInstanceId {
    Transition(Tid),
    Place(Pid, usize),
}

pub struct EditList {
    pub tids: Set<Tid>,
    pub pids: Set<Pid>,
}

impl EditList {
    pub fn new() -> EditList {
        EditList {
            pids: Set::new(),
            tids: Set::new(),
        }
    }
}

pub struct SelectionMap<Id>(Map<Id, Set<usize>>);

impl<Id: Eq + Hash> SelectionMap<Id> {
    fn new() -> Self {
        Self(Map::new())
    }

    pub fn insert(&mut self, index: Id, subindex: usize) {
        if let Some(set) = self.0.get_mut(&index) {
            set.insert(subindex);
        } else {
            let mut set = Set::new();
            set.insert(subindex);
            self.0.insert(index, set);
        }
    }

    pub fn contains(&self, index: &Id, subindex: &usize) -> bool {
        if let Some(set) = self.0.get(index) {
            set.contains(subindex)
        } else {
            false
        }
    }

    pub fn contains_key(&self, index: &Id) -> bool {
        self.0.contains_key(index)
    }

    pub fn iter(&self) -> impl Iterator<Item = (&Id, &Set<usize>)> {
        self.0.iter()
    }

    pub fn keys(&self) -> impl Iterator<Item = &Id> {
        self.0.keys()
    }

    fn remove(&mut self, index: &Id, subindex: &usize) {
        if let Some(set) = self.0.get_mut(index) {
            set.remove(subindex);
            if set.is_empty() {
                self.0.remove(index);
            }
        }
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn len(&self) -> usize {
        self.0.len()
    }

    pub fn is_single(&self) -> bool {
        if self.len() != 1 {
            return false;
        }
        self.iter().next().unwrap().1.len() == 1
    }

    fn clear(&mut self) {
        self.0.clear()
    }
}

pub struct SelectionList {
    pub tids: Set<Tid>,
    pub pids: SelectionMap<Pid>,
}

impl SelectionList {
    pub fn new() -> SelectionList {
        SelectionList {
            pids: SelectionMap::new(),
            tids: Set::new(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.pids.is_empty() && self.tids.is_empty()
    }

    pub fn is_single(&self) -> bool {
        (self.pids.is_empty() && self.tids.len() == 1)
            || (self.pids.is_single() && self.tids.is_empty())
    }

    pub fn clear(&mut self) {
        self.pids.clear();
        self.tids.clear();
    }

    pub fn add(&mut self, id: NodeInstanceId) {
        use NodeInstanceId::*;
        match id {
            Place(id, subindex) => self.pids.insert(id, subindex),
            Transition(id) => {
                self.tids.insert(id);
            }
        }
    }

    pub fn contains(&mut self, id: &NodeInstanceId) -> bool {
        use NodeInstanceId::*;
        match id {
            Place(id, subindex) => self.pids.contains(id, subindex),
            Transition(id) => self.tids.contains(id),
        }
    }

    pub fn remove_node(&mut self, id: NodeInstanceId) {
        use NodeInstanceId::*;
        match id {
            Place(id, subindex) => self.pids.remove(&id, &subindex),
            Transition(id) => {
                self.tids.remove(&id);
            }
        }
    }
}
