use super::{snap, Editor};

use crate::{
    edit_list::NodeInstanceId,
    feature_types::{DuplicationType, Grab, GrabKind, MoveType, ViewMode},
    vector::Vector,
};

use pns::{Pid, SafeNet};

impl Editor {
    fn snapped_relative_mouse_pos(&self) -> Vector {
        let mut new_pos = self.relative_mouse_pos();
        if let Some(snap_size) = self.snapping {
            snap(&mut new_pos, snap_size);
        }
        new_pos
    }

    pub(super) fn add_place_linked(
        &mut self,
        pid: Pid,
        positions: &[Vector],
    ) -> Vec<NodeInstanceId> {
        self.changes = true;
        let node = self.nodes.place_mut(pid);
        let old_len = node.positions.len();
        node.positions.extend(positions);
        (0..positions.len())
            .map(|subindex| NodeInstanceId::Place(pid, subindex + old_len))
            .collect()
    }

    pub(super) fn grab_node(
        &mut self,
        node_id: NodeInstanceId,
        special: bool,
        clear_old: bool,
        duplicate: bool,
    ) {
        self.changes = true;
        let grab_pos = self.relative_mouse_pos();
        let contains_node = self.selected.contains(&node_id);
        if clear_old && !contains_node {
            self.selected.clear();
        }
        self.selected.add(node_id);
        use GrabKind::*;
        let grab_kind = if special {
            Connect
        } else {
            Move(MoveType::Nodes)
        };
        let duplicate = if duplicate && !special {
            Some(if clear_old {
                DuplicationType::New
            } else {
                DuplicationType::Linked
            })
        } else {
            None
        };
        self.grab = Some(Grab {
            pos: grab_pos,
            kind: grab_kind,
            moved: false,
            deselect: (self.selected.is_single() || !clear_old) && contains_node,
            duplicate,
        });
    }

    pub(super) fn grab_empty(&mut self, special: bool, clear_old: bool, create: bool) {
        if create {
            self.changes = true;
            let net = self.net.safe_mut();
            if special {
                if self.view_mode == ViewMode::Actions {
                    return;
                }
                let pid = net.add_place();
                let new_nodes = self.add_place(pid, &[self.relative_mouse_pos()], None);
                for node in new_nodes {
                    self.selected.add(node);
                }
            } else {
                if self.view_mode == ViewMode::State {
                    return;
                }
                let tid = net.add_transition();
                let new_node = self.add_transition(tid, self.relative_mouse_pos(), None);
                self.selected.add(new_node);
            };
            if clear_old {
                self.selected.clear();
            }
            self.grab = Some(Grab {
                pos: self.relative_mouse_pos(),
                kind: GrabKind::Move(MoveType::Nodes),
                moved: false,
                deselect: false,
                duplicate: None,
            });
        } else {
            self.grab = Some(Grab {
                pos: self.relative_mouse_pos(),
                kind: if special {
                    GrabKind::Select
                } else {
                    GrabKind::Move(MoveType::View)
                },
                moved: false,
                deselect: false,
                duplicate: None,
            });
        }
    }

    fn connect_place(
        &mut self,
        connect_id: &NodeInstanceId,
        new_nodes: &mut Vec<NodeInstanceId>,
        allow_more: bool,
    ) -> bool {
        let mut new_transitions = Vec::new();
        let mut new_node = false;
        for (&pid, subindices) in self.selected.pids.iter() {
            use NodeInstanceId::*;
            match connect_id {
                &Transition(connect_tid) => {
                    let connected = if let Some(net) = self.net.default_mut() {
                        net.connect_in(connect_tid, pid)
                    } else {
                        false
                    };
                    if !connected && allow_more {
                        self.net.safe_mut().disconnect_in(connect_tid, pid);
                    }
                }
                &Place(connect_pid, subindex) => {
                    if pid != connect_pid || allow_more {
                        let node = &self.nodes.place(pid);
                        let node_pos: Vector =
                            subindices.iter().map(|&tid| node.positions[tid]).sum();
                        let node_pos = node_pos / subindices.len() as f32;
                        let other = &self.nodes.place(connect_pid);
                        let other_pos = other.positions[subindex];
                        let new_pos = (node_pos + other_pos) / 2.0;
                        if let Some(net) = self.net.default_mut() {
                            let new_id = net.add_transition();
                            net.connect_in(new_id, pid);
                            net.connect_out(new_id, connect_pid);
                            new_transitions.push((new_id, new_pos));
                        }
                    } else {
                        new_node = true
                    }
                }
            }
        }
        for (new_id, new_pos) in new_transitions {
            let transition = self.add_transition(new_id, new_pos, None);
            new_nodes.push(transition);
        }
        new_node
    }

    fn connect_transition(
        &mut self,
        connect_id: &NodeInstanceId,
        new_nodes: &mut Vec<NodeInstanceId>,
        allow_more: bool,
    ) -> bool {
        let mut new_places = Vec::new();
        let mut new_node = false;
        for &tid in self.selected.tids.iter() {
            use NodeInstanceId::*;
            match connect_id {
                &Place(connect_pid, _subindex) => {
                    let connected = self.net.safe_mut().connect_out(tid, connect_pid);
                    if !connected && allow_more {
                        if let Some(net) = self.net.default_mut() {
                            net.disconnect_out(tid, connect_pid);
                        }
                    }
                }
                &Transition(connect_tid) => {
                    if tid != connect_tid || allow_more {
                        let node = &self.nodes.transitions.node(tid);
                        let other = &self.nodes.transitions.node(connect_tid);
                        let new_pos = (node.position + other.position) / 2.0;
                        if let Some(net) = self.net.default_mut() {
                            let new_id = net.add_place();
                            net.connect_in(connect_tid, new_id);
                            net.connect_out(tid, new_id);
                            new_places.push((new_id, new_pos));
                        }
                    } else {
                        new_node = true;
                    }
                }
            }
        }
        for (new_id, new_pos) in new_places {
            let place = self.add_place(new_id, &[new_pos], None);
            new_nodes.extend(place);
        }
        new_node
    }

    pub(super) fn release_connect(&mut self, allow_more: bool, clear_old: bool, select_new: bool) {
        let mut new_nodes = Vec::new();
        if let Some(connect_node) = self.click_info(self.relative_mouse_pos()) {
            if self.connect_place(&connect_node, &mut new_nodes, allow_more)
                || self.connect_transition(&connect_node, &mut new_nodes, allow_more)
            {
                new_nodes.push(connect_node);
            }
        } else if allow_more {
            match self.view_mode {
                ViewMode::Default => {
                    if self.selected.pids.len() > 0 {
                        let pids: Vec<_> = self.selected.pids.keys().cloned().collect();
                        let connect_id = self.net.safe_mut().add_connected_transition(&pids[..]);
                        let transition = self.add_transition(
                            connect_id,
                            self.snapped_relative_mouse_pos(),
                            None,
                        );
                        new_nodes.push(transition);
                    }
                    if self.selected.tids.len() > 0 {
                        let connect_id = self.net.safe_mut().add_place();
                        let place =
                            self.add_place(connect_id, &[self.snapped_relative_mouse_pos()], None);
                        new_nodes.extend(place);
                        for &tid in &self.selected.tids {
                            self.net.safe_mut().connect_out(tid, connect_id);
                        }
                    }
                }
                ViewMode::State => {
                    let connect_id = self.net.safe_mut().add_place();
                    let new_pos = self.snapped_relative_mouse_pos();
                    let place = self.add_place(connect_id, &[new_pos], None);
                    new_nodes.extend(place);
                    let selected_pids: Vec<_> = self.selected.pids.keys().cloned().collect();
                    for pid in selected_pids {
                        let node = &self.nodes.place(pid);
                        if let Some((pos, _)) = node.positions.iter().fold(None, |result, pos| {
                            let new_distance = (pos - self.relative_mouse_pos()).norm_squared();
                            if let Some((position, distance)) = result {
                                if new_distance >= distance {
                                    return Some((position, distance));
                                }
                            }
                            Some((*pos, new_distance))
                        }) {
                            let new_id = self.net.safe_mut().add_connected_transition(&[pid]);
                            let transition =
                                self.add_transition(new_id, (pos + new_pos) / 2.0, None);
                            self.net.safe_mut().connect_out(new_id, connect_id);
                            new_nodes.push(transition);
                        }
                    }
                }
                ViewMode::Actions => {
                    let mut new_ids = Vec::new();
                    let selected_tids: Vec<_> = self.selected.tids.iter().cloned().collect();
                    let new_pos = self.snapped_relative_mouse_pos();
                    for tid in selected_tids {
                        let node_pos = self.nodes.transitions.node(tid).position;
                        let new_id = self.net.safe_mut().add_place();
                        self.net.safe_mut().connect_out(tid, new_id);
                        let place = self.add_place(new_id, &[(node_pos + new_pos) / 2.0], None);
                        new_nodes.extend(place);
                        new_ids.push(new_id);
                    }
                    let connect_id = self.net.safe_mut().add_connected_transition(&new_ids[..]);
                    let transition = self.add_transition(connect_id, new_pos, None);
                    new_nodes.push(transition);
                }
            }
        }
        if clear_old {
            self.selected.clear()
        }
        if select_new {
            for node in new_nodes {
                self.selected.add(node);
            }
        }
    }
}
