use super::Editor;

use crate::vector::Vector;

use copypasta::ClipboardProvider;
use pns::Pid;
use text_editing::TextLine;

use std::collections::HashMap;

impl Editor {
    fn copy_data(&self) -> Option<String> {
        if self.selected.is_empty() {
            None
        } else {
            let mut result = "".to_string();
            let mut place_mappings = HashMap::new();
            let transition_count = self.selected.tids.len();
            for pid in self.selected.pids.keys() {
                place_mappings.insert(pid, place_mappings.len());
            }
            result = format!("{}{}\n", result, place_mappings.len());
            for (&pid, subindices) in self.selected.pids.iter() {
                let node = &self.nodes.place(pid);
                let name = node.name.to_string();
                let count = self.net.safe().initial_token_count(pid);
                result = format!("{}{}\n{}\n{}\n", result, name, count, subindices.len());
                for &subindex in subindices {
                    let pos = &node.positions[subindex];
                    let pos = pos - self.relative_mouse_pos();
                    result = format!("{}{} {}\n", result, pos.x, pos.y);
                }
            }
            result = format!("{}{}\n", result, transition_count);
            for &tid in self.selected.tids.iter() {
                let node = &self.nodes.transitions.node(tid);
                let name = node.name.to_string();
                result = format!("{}{}\n", result, name);
                let pos = node.position - self.relative_mouse_pos();
                result = format!("{}{} {}\n", result, pos.x, pos.y);
                let transition = &self.net.safe().transition(tid);
                for pid in transition.prev() {
                    if let Some(mapped_pid) = place_mappings.get(pid) {
                        result = format!("{}{} ", result, mapped_pid);
                    }
                }
                result = format!("{}\n", result);
                for pid in transition.next() {
                    if let Some(mapped_pid) = place_mappings.get(pid) {
                        result = format!("{}{} ", result, mapped_pid);
                    }
                }
                result = format!("{}\n", result);
            }
            Some(format!("Petri Net:\n{}", result))
        }
    }

    fn paste_data(&mut self, data: &str, clear_old: bool) -> Option<()> {
        self.changes = true;
        let mut lines = data.lines();
        let kind = lines.next()?;
        if kind.trim_end() != "Petri Net:" {
            return None;
        }
        let mut new_nodes = Vec::new();
        if let Ok(place_count) = lines.next()?.parse::<usize>() {
            let mut new_places = HashMap::new();
            for i in 0..place_count {
                let name = lines.next()?;
                let count = lines.next()?.parse::<usize>().ok()?;
                let mut positions = Vec::new();
                for _ in 0..lines.next()?.parse::<usize>().ok()? {
                    let mut pos_line = lines.next()?.split_whitespace();
                    let offset_x = pos_line.next()?.parse::<f32>().ok()?;
                    let offset_y = pos_line.next()?.parse::<f32>().ok()?;
                    positions.push(self.relative_mouse_pos() + Vector::new(offset_x, offset_y));
                }
                let index = self.net.safe_mut().add_place();
                self.net.safe_mut().start(index, count);
                let nodes = self.add_place(index, &positions[..], None);
                new_places.insert(i, index);
                self.nodes.place_mut(index).name = TextLine::from_str(name);
                new_nodes.extend(nodes);
            }
            if let Ok(transition_count) = lines.next()?.parse::<usize>() {
                for _i in 0..transition_count {
                    let name = lines.next()?;
                    let mut pos_line = lines.next()?.split_whitespace();
                    let offset_x = pos_line.next()?.parse::<f32>().ok()?;
                    let offset_y = pos_line.next()?.parse::<f32>().ok()?;
                    let position = self.relative_mouse_pos() + Vector::new(offset_x, offset_y);
                    let ins: Vec<Pid> = lines
                        .next()?
                        .split_whitespace()
                        .map(|index| new_places[&index.parse::<usize>().ok().unwrap().into()])
                        .collect();
                    let outs = lines
                        .next()?
                        .split_whitespace()
                        .map(|index| new_places[&index.parse::<usize>().ok().unwrap().into()]);
                    let net = &mut self.net.safe_mut();
                    let index = net.add_connected_transition(&ins[..]);
                    for out in outs {
                        net.connect_out(index, out);
                    }
                    let node =
                        self.add_transition(index, position, self.transition_context.clone(), None);
                    self.nodes.transitions.node_mut(index).name = TextLine::from_str(name);
                    new_nodes.push(node);
                }
            }
        }
        if clear_old {
            self.selected.clear();
        }
        for node in new_nodes {
            self.selected.add(node);
        }
        Some(())
    }

    /// Copy selected elements to clipboard.
    pub fn copy_selected(&mut self) {
        if let Some(data) = self.copy_data() {
            let _result = self.clipboard.set_contents(data);
        }
    }

    /// Cut selected elements to clipboard.
    pub fn cut_selected(&mut self) {
        self.copy_selected();
        self.remove_nodes()
    }

    /// Pastes contents from clipboard.
    pub fn paste(&mut self, clear_old: bool) {
        if let Ok(content) = self.clipboard.get_contents() {
            self.paste_data(&content, clear_old);
        }
    }
}
