use crate::tree_config::{tree_config, TreeConfig};
use std::cmp::max;
use std::sync::{Arc, Mutex};

/// Tree that holds `text` for the current leaf and a list of `children` that are the branches.
#[derive(Debug)]
pub struct Tree {
    pub text: Option<String>,
    pub children: Vec<Tree>,
}

/// Position of the element relative to its siblings
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Position {
    Inside,
    First,
    Last,
    Only,
}

impl Tree {
    /// Create a new tree with some optional text.
    pub fn new(text: Option<&str>) -> Tree {
        Tree {
            text: text.map(|x| x.to_string()),
            children: Vec::new(),
        }
    }

    /// Navigate to the branch at the given `path` relative to this tree.
    /// If a valid branch is found by following the path, it is returned.
    pub fn at_mut(&mut self, path: &[usize]) -> Option<&mut Tree> {
        match path.first() {
            Some(&i) => match self.children.get_mut(i) {
                Some(x) => x.at_mut(&path[1..]),
                _ => None,
            },
            _ => Some(self),
        }
    }

    /// "Render" this tree as a list of `String`s.
    /// Each string represents a line in the tree.
    /// `does_continue` is a bool for each column indicating whether the tree continues.
    pub fn lines(
        &self,
        does_continue: &Vec<bool>,
        index: usize,
        pool_size: usize,
        config: &TreeConfig,
    ) -> Vec<String> {
        let does_continue = if config.show_first_level && does_continue.is_empty() {
            vec![true]
        } else {
            does_continue.clone()
        };
        let position = match index {
            _ if pool_size == 1 => Position::Only,
            _ if (index + 1) == pool_size => Position::Last,
            0 => Position::First,
            _ => Position::Inside,
        };
        let mut next_continue = does_continue.clone();
        next_continue.push(match position {
            Position::Inside | Position::First => true,
            Position::Last | Position::Only => false,
        });

        let mut txt = String::new();
        let pad: String;
        if does_continue.len() > 1 {
            for &i in &does_continue[2..] {
                txt.push_str(&format!(
                    "{}{:indent$}",
                    if i { config.symbols.continued } else { " " },
                    "",
                    indent = max(config.indent, 1) - 1
                ));
            }
            pad = txt.clone();
            let branch_size = max(config.indent, 2usize) - 2;
            let branch = match config.symbols.branch.len() {
                0 => "-".repeat(branch_size),
                1 => config.symbols.branch.repeat(branch_size),
                _n => config
                    .symbols
                    .branch
                    .repeat(branch_size)
                    .chars()
                    .take(branch_size)
                    .collect::<String>(),
            };

            let is_multiline = self
                .text
                .as_ref()
                .map(|x| x.contains("\n"))
                .unwrap_or(false);

            let first_leaf = match (is_multiline, config.symbols.multiline_first) {
                (true, Some(x)) => x,
                _ => config.symbols.leaf,
            };
            txt.push_str(&format!(
                "{}{}{}",
                match position {
                    Position::Only => config.symbols.join_only,
                    Position::First => config.symbols.join_first,
                    Position::Last => config.symbols.join_last,
                    Position::Inside => config.symbols.join_inner,
                },
                branch,
                first_leaf,
            ));

            let s = match &self.text {
                Some(x) => match is_multiline {
                    true => format!(
                        "{}",
                        x.replace(
                            "\n",
                            &format!(
                                "\n{}{}{}{}",
                                &pad,
                                match position {
                                    Position::Only | Position::Last =>
                                        " ".repeat(config.symbols.continued.chars().count()),
                                    _ => config.symbols.continued.to_string(),
                                },
                                " ".repeat(branch_size),
                                match &config.symbols.multiline_continued {
                                    Some(multi) => multi.to_string(),
                                    _ => " ".repeat(first_leaf.chars().count()),
                                }
                            ),
                        )
                    ),
                    false => x.clone(),
                },
                _ => String::new(),
            };
            txt.push_str(&s);
        } else {
            if let Some(x) = &self.text {
                txt.push_str(&x);
            }
        }
        let mut ret = vec![txt];
        for (index, x) in self.children.iter().enumerate() {
            for line in x.lines(&next_continue, index, self.children.len(), config) {
                ret.push(line);
            }
        }
        ret
    }
}

/// Holds the current state of the tree, including the path to the branch.
/// Multiple trees may point to the same data.
#[derive(Debug, Clone)]
pub(crate) struct TreeBuilderBase {
    data: Arc<Mutex<Tree>>,
    path: Vec<usize>,
    dive_count: usize,
    config: Option<TreeConfig>,
    is_enabled: bool,
}

impl TreeBuilderBase {
    /// Create a new state
    pub fn new() -> TreeBuilderBase {
        TreeBuilderBase {
            data: Arc::new(Mutex::new(Tree::new(None))),
            path: vec![],
            dive_count: 1,
            config: None,
            is_enabled: true,
        }
    }

    pub fn set_enabled(&mut self, enabled: bool) {
        self.is_enabled = enabled;
    }
    pub fn is_enabled(&self) -> bool {
        self.is_enabled
    }

    pub fn add_leaf(&mut self, text: &str) {
        let &dive_count = &self.dive_count;
        if dive_count > 0 {
            for i in 0..dive_count {
                let mut n = 0;
                if let Some(x) = self.data.lock().unwrap().at_mut(&self.path) {
                    x.children.push(Tree::new(if i == max(1, dive_count) - 1 {
                        Some(&text)
                    } else {
                        None
                    }));
                    n = x.children.len() - 1;
                }
                self.path.push(n);
            }
            self.dive_count = 0;
        } else {
            if let Some(x) = self
                .data
                .lock()
                .unwrap()
                .at_mut(&self.path[..max(1, self.path.len()) - 1])
            {
                x.children.push(Tree::new(Some(&text)));
                let n = match self.path.last() {
                    Some(&x) => x + 1,
                    _ => 0,
                };
                self.path.last_mut().map(|x| *x = n);
            }
        }
    }

    pub fn set_config_override(&mut self, config: Option<TreeConfig>) {
        self.config = config;
    }

    pub fn config_override(&self) -> &Option<TreeConfig> {
        &self.config
    }
    pub fn config_override_mut(&mut self) -> &mut Option<TreeConfig> {
        &mut self.config
    }

    pub fn enter(&mut self) {
        self.dive_count += 1;
    }

    /// Try stepping up to the parent tree branch.
    /// Returns false if already at the top branch.
    pub fn exit(&mut self) -> bool {
        if self.dive_count > 0 {
            self.dive_count -= 1;
            true
        } else {
            if self.path.len() > 1 {
                self.path.pop();
                true
            } else {
                false
            }
        }
    }

    pub fn depth(&self) -> usize {
        max(1, self.path.len() + self.dive_count) - 1
    }

    pub fn peek_print(&self) {
        println!("{}", self.peek_string());
    }

    pub fn print(&mut self) {
        self.peek_print();
        self.clear();
    }
    pub fn clear(&mut self) {
        *self = Self::new();
    }

    pub fn string(&mut self) -> String {
        let s = self.peek_string();
        self.clear();
        s
    }

    pub fn peek_string(&self) -> String {
        let config = self
            .config_override()
            .clone()
            .unwrap_or_else(|| tree_config().clone());
        (&self.data.lock().unwrap().lines(&vec![], 0, 1, &config)[1..]).join("\n")
    }
}