mod copypaste;
mod files;
mod grab;
mod text;

use super::Editor;

use crate::{
    container_map::ContainerMap,
    edit_list::{EditList, NodeInstanceId, SelectionList},
    feature_types::{SearchId, StringSearcher, ViewMode},
    node::{NodeContainer, PlaceNode, TransitionNode},
    state_info::StateInfo,
    vector::Vector,
};

use pns::{DynamicNet, Net, Pid, SafeNet, SimulatedNet, Tid};
use rfd::{MessageButtons, MessageDialog, MessageLevel};
use text_editing::TextLine;

use std::collections::{BTreeMap, HashMap};

fn snap(pos: &mut Vector, snap_size: f32) {
    pos.x = (pos.x / snap_size).round() * snap_size;
    pos.y = (pos.y / snap_size).round() * snap_size;
}

impl Editor {
    fn next_transition_name(&mut self) -> String {
        let name = if self.transition_index == 0 {
            "Intro".to_string()
        } else {
            format!("Act {}", self.transition_index)
        };
        self.transition_index += 1;
        name
    }

    fn reset(&mut self, mut net: Net, paths: Vec<Vec<String>>, place_names: Vec<String>) {
        for _ in (net.place_count() as usize)..place_names.len() {
            net.add_place();
        }

        for _ in (net.transition_count() as usize)..paths.len() {
            net.add_transition();
        }

        self.nodes = NodeContainer::new(BTreeMap::new(), BTreeMap::new());

        self.transition_index = 0;

        let mut place_names = place_names.into_iter();
        let place_count = net.place_count();
        for (i, pid) in net.place_ids().enumerate() {
            let name = place_names.next().unwrap_or(String::new());
            let node = PlaceNode::new(
                &[Vector::new(
                    self.node_settings.width,
                    self.node_settings.height * (i as i32 * 2 - place_count as i32) as f32,
                )],
                name,
            );
            self.nodes.add_place(pid, node);
        }

        let mut path_ids = HashMap::new();
        let mut names = paths.iter().cloned();
        let transition_count = net.transition_count();
        for (i, tid) in net.transition_ids().enumerate() {
            let path = names.next().unwrap_or_else(|| Vec::new());
            let name = path
                .last()
                .cloned()
                .unwrap_or_else(|| self.next_transition_name());
            let node = TransitionNode::new(
                Vector::new(
                    -self.node_settings.width,
                    self.node_settings.height * (i as i32 * 2 - transition_count as i32) as f32,
                ),
                name,
            );
            self.nodes.transitions.insert(tid, node);
            path_ids.insert(path, tid);
        }
        for (tid, mut path) in net.transition_ids().zip(paths) {
            if path.pop().is_some() {
                if let Some(&parent) = path_ids.get(&path) {
                    self.nodes.transitions.map.put(tid, parent);
                }
            }
        }

        self.net = net.dynamic();

        self.state_infos = Vec::new();

        self.selected = SelectionList::new();
        self.view_offset = Vector::new(0.0, 0.0);

        self.simulation = 0;
        self.changes = false;
    }

    fn line_count(&self) -> usize {
        ((self.window_size.y / self.node_settings.height * 2.0) as usize).saturating_sub(5)
    }

    fn add_transition(
        &mut self,
        tid: Tid,
        position: Vector,
        name: Option<String>,
    ) -> NodeInstanceId {
        self.changes = true;
        let new_node = TransitionNode::new(
            position,
            if let Some(name) = name {
                name
            } else {
                self.next_transition_name()
            },
        );
        self.nodes.transitions.insert(tid, new_node);
        if let DynamicNet::Simulated(net) = &mut self.net {
            for (info, state) in &mut self.state_infos.iter_mut().zip(&mut net.states) {
                if !info.callables.contains_key(&tid) {
                    info.callables.insert(tid, (false, false));
                    info.update_state(state);
                }
            }
        }
        NodeInstanceId::Transition(tid)
    }

    fn add_place(
        &mut self,
        pid: Pid,
        positions: &[Vector],
        name: Option<String>,
    ) -> Vec<NodeInstanceId> {
        self.changes = true;
        let new_node = PlaceNode::new(
            positions,
            if let Some(name) = name {
                name
            } else {
                String::new()
            },
        );
        self.nodes.add_place(pid, new_node);
        (0..positions.len())
            .map(|subindex| NodeInstanceId::Place(pid, subindex))
            .collect()
    }

    /// Switches simulation mode.
    pub fn switch_mode(&mut self) {
        use DynamicNet::*;
        if !self.ensure_saved(true) {
            return;
        }
        replace_with::replace_with_or_abort(&mut self.net, |old_net| match old_net {
            Default(net) => Simulated(SimulatedNet::from_net(net)),
            Simulated(net) => Default(net.release()),
        });
        if let Simulated(net) = &mut self.net {
            net.add_state();
            let mut info = StateInfo {
                state_path: None,
                changes: false,
                callables: net
                    .transition_ids()
                    .map(|tid| (tid, (false, false)))
                    .collect(),
            };
            self.simulation = 0;
            info.update_state(&mut net.states[0]);
            self.state_infos.push(info);
        } else {
            self.state_infos = Vec::new();
        }
    }

    fn switch_simulation(&mut self) {
        if let Some(net) = self.net.simulated() {
            self.simulation += 1;
            if self.simulation == net.len() {
                self.simulation = 0;
            }
        }
    }

    // edit

    /// Adds a token to nodes if they are places.
    pub fn start_nodes(&mut self) {
        self.changes = true;
        for &pid in self.selected.pids.keys() {
            let net = self.net.safe_mut();
            net.start(pid, 1);
        }
    }

    /// Removes all selected nodes.
    pub fn remove_nodes(&mut self) {
        self.changes = true;
        if self.view_mode != ViewMode::Actions {
            for (&pid, subindices) in self.selected.pids.iter() {
                let positions = &mut self.nodes.place_mut(pid).positions;
                for index in subindices.iter().rev() {
                    positions.remove(*index);
                }
                if positions.is_empty() {
                    self.net.safe_mut().remove_place(pid);
                    self.nodes.remove_place(pid);
                }
            }
        }
        if self.view_mode != ViewMode::State {
            if let Some(net) = self.net.default_mut() {
                for &tid in &self.selected.tids {
                    net.remove_transition(tid);
                    self.nodes.transitions.remove(tid);
                }
            }
        }
        self.selected.clear()
    }

    /// Should be called when the window is resized.
    pub fn resize(&mut self, width: u32, height: u32) {
        self.window_size = Vector::new(width as f32, height as f32);
    }

    /// Cycle through states.
    pub fn cycle(&mut self, skip: bool) {
        if let Some(string_searcher) = &mut self.string_searcher {
            if let Some(index) = &mut string_searcher.index {
                use SearchId::*;
                match index {
                    Place(pindex) => {
                        if skip {
                            if !string_searcher.list.tids.is_empty() {
                                *index = Transition(0);
                            }
                        } else {
                            *pindex += 1;
                            if string_searcher.list.pids.len() == *pindex {
                                *index = if string_searcher.list.tids.is_empty() {
                                    Place(0)
                                } else {
                                    Transition(0)
                                }
                            }
                        }
                    }
                    Transition(tindex) => {
                        if skip {
                            if !string_searcher.list.pids.is_empty() {
                                *index = Place(0);
                            }
                        } else {
                            *tindex += 1;
                            if string_searcher.list.tids.len() == *tindex {
                                *index = if string_searcher.list.pids.is_empty() {
                                    Transition(0)
                                } else {
                                    Place(0)
                                }
                            }
                        }
                    }
                }
            }
        } else {
            self.switch_simulation()
        }
    }

    /// Adds a new simulation state.
    pub fn add_state(&mut self) {
        if let DynamicNet::Simulated(net) = &mut self.net {
            net.add_state();
            let new_index = net.states.len() - 1;
            let mut info = StateInfo {
                state_path: None,
                changes: false,
                callables: net
                    .transition_ids()
                    .map(|tid| (tid, (false, false)))
                    .collect(),
            };
            self.simulation = new_index;
            info.update_state(&mut net.state_mut(new_index).state);
            self.state_infos.push(info);
        }
    }

    /// Toggles snapping.
    pub fn toggle_snapping(&mut self) {
        if self.snapping.is_some() {
            self.snapping = None;
        } else {
            self.snapping = Some(16.0)
        }
    }

    /// Toggles the editor.
    pub fn toggle_editor(&mut self) {
        self.text_content_shown = !self.text_content_shown;
        self.string_searcher = None;
    }

    /// Enables the search bar.
    pub fn start_search_mode(&mut self) {
        if self.string_searcher.is_none() {
            self.text_content_shown = false;
            self.string_searcher = Some(StringSearcher {
                name: TextLine::new(),
                name_cursor: 0,
                index: None,
                list: EditList::new(),
            });
        }
    }

    /// Loads in an empty net.
    pub fn load_new(&mut self) {
        if self.ensure_saved(false) {
            self.net_path = None;
            self.reset(Net::new(), Vec::new(), Vec::new())
        }
    }

    /// Changes the view mode.
    /// See `ViewMode` for more information about the view modes.
    pub fn set_view_mode(&mut self, view_mode: ViewMode) {
        self.view_mode = view_mode;
    }

    /// Zoom in or out by a factor.
    pub fn zoom(&mut self, factor: f32, zoom_scale: bool, zoom_distance: bool) {
        self.view_offset = self.relative_mouse_pos();
        if zoom_scale {
            self.zoom *= factor;
        }

        if zoom_distance || !zoom_scale {
            fn zoom_position(position: &mut Vector, view_offset: Vector, factor: f32) {
                *position -= view_offset;
                *position *= factor;
                *position += view_offset;
            }

            fn zoom_transitions(
                transitions: &ContainerMap<Tid>,
                nodes: &mut BTreeMap<Tid, TransitionNode>,
                view_offset: Vector,
                factor: f32,
            ) {
                for (tid, children) in transitions.iter_tree() {
                    zoom_position(
                        &mut nodes.get_mut(&tid).unwrap().position,
                        view_offset,
                        factor,
                    );
                    zoom_transitions(children, nodes, view_offset, factor)
                }
            }
            zoom_transitions(
                &self.nodes.transitions.map,
                &mut self.nodes.transitions.nodes,
                self.view_offset,
                factor,
            );

            for (_, node) in self.nodes.iter_places_mut() {
                for position in &mut node.positions {
                    zoom_position(position, self.view_offset, factor);
                }
            }
        }
        self.view_offset = self.view_offset * 2.0 - self.relative_mouse_pos();
    }

    /// Ensure the net be saved.
    pub fn ensure_saved(&mut self, state_only: bool) -> bool {
        let mut ask = if state_only { false } else { self.changes };
        if !ask {
            for info in &self.state_infos {
                if info.changes {
                    ask = true;
                    break;
                }
            }
        }
        if ask {
            MessageDialog::new()
                .set_level(MessageLevel::Warning)
                .set_title("Warning!")
                .set_description("There are unsaved changes, that will be lost\nAre you sure?")
                .set_buttons(MessageButtons::YesNo)
                .show()
        } else {
            true
        }
    }
}
