use crate::goban::{Coordinate, Goban};
use config::define_config;
use config_derive::ConfigOption;
use nary_tree::NodeRef;
use serde::{Deserialize, Serialize};
use sgf::GameTree;
use std::{
    collections::{HashMap, VecDeque}, fmt, ops::Deref, path::PathBuf, time::Duration
};
use thiserror::Error;

define_config! {
    LibraryPath(LibraryPath),
    Me(Me),
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)]
pub struct LibraryPath(pub PathBuf);

impl std::ops::Deref for LibraryPath {
    type Target = PathBuf;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<String> for LibraryPath {
    fn from(s: String) -> Self {
        Self(PathBuf::from(s))
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)]
pub struct Me(Player);

impl std::ops::Deref for Me {
    type Target = Player;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Debug, PartialEq, Error)]
pub enum BoardError {
    #[error("Position is invalid")]
    InvalidPosition,
    #[error("Self-capture is forbidden")]
    SelfCapture,
    #[error("Ko")]
    Ko,
}

#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)]
pub enum Color {
    Black,
    White,
}

impl From<&sgf::Color> for Color {
    fn from(c: &sgf::Color) -> Self {
        match c {
            sgf::Color::Black => Self::Black,
            sgf::Color::White => Self::White,
        }
    }
}

impl fmt::Display for Color {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
        match self {
            Color::Black => write!(formatter, "Black"),
            Color::White => write!(formatter, "White"),
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Size {
    pub width: u8,
    pub height: u8,
}

impl Default for Size {
    fn default() -> Self {
        Self {
            width: 19,
            height: 19,
        }
    }
}

/// AppState stores all of the important state to a full running version of the application.
/// However, this version of AppState is in pretty sorry shape.
///
/// What are the states of the app?
///
/// - in review
/// - in a game
/// - connections to the internet
/// - connections to local applications, such as Leela Zero
/// - the current configuration
/// - the games database
/// - If in a game, the current state of the game. Delegated to GameState.
/// - If in review, the current state of the review.
///
/// Some of these states are concurrent. It's quite possible to have online connections running and
/// to be reviewing a game while, for instance, waiting for an opponent on OGS to make a move.
///
/// I get to ignore a lot of these things for now. Not playing online. Not playing at all,
/// actually. We'll come back to that.
///
/// Plus, it gets more fuzzy, because some of the application state is really UI state. For
/// instance, the state of a game review is purely UI.
///
/// So... AppState probably isn't great for now, but maybe it will become so later. I think I'm
/// going to ignore it until I need it.
/*
#[derive(Debug)]
pub struct AppState {
    pub game: Option<GameState>,
    pub database: Database,
}

impl AppState {
    pub fn new(database_path: LibraryPath) -> Self {
        Self {
            game: Some(GameState::default()),
            database: Database::open_path(database_path.to_path_buf()).unwrap(),
        }
    }

    /*
    pub fn place_stone(&mut self, req: PlayStoneRequest) {
        if let Some(ref mut game) = self.game {
            let _ = game.place_stone(Coordinate {
                column: req.column,
                row: req.row,
            });
        }
    }
    */
}
*/

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Rank {
    Kyu(u8),
    Dan(u8),
    Pro(u8),
}

impl TryFrom<&str> for Rank {
    type Error = String;

    fn try_from(_: &str) -> Result<Rank, Self::Error> {
        Ok(Rank::Kyu(15))
    }
}

impl From<Rank> for String {
    fn from(r: Rank) -> String {
        match r {
            Rank::Kyu(v) => format!("{} kyu", v),
            Rank::Dan(v) => format!("{} dan", v),
            Rank::Pro(v) => format!("{} pro", v),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Player {
    pub name: String,
    pub rank: Option<Rank>,
}

#[derive(Debug)]
pub struct GameState {
    pub board: Goban,
    pub past_positions: Vec<Goban>,

    pub conversation: Vec<String>,
    pub current_player: Color,

    pub white_player: Player,
    pub black_player: Player,

    pub white_clock: Duration,
    pub black_clock: Duration,
}

impl Default for GameState {
    fn default() -> Self {
        Self {
            board: Goban::new(),
            past_positions: vec![],
            conversation: vec![],
            current_player: Color::Black,
            white_player: Player {
                name: "".to_owned(),
                rank: None,
            },
            black_player: Player {
                name: "".to_owned(),
                rank: None,
            },
            white_clock: Duration::from_secs(600),
            black_clock: Duration::from_secs(600),
        }
    }
}

impl GameState {
    // Legacy code. I recall that this is no longer used (but will be used again) because I
    // commented out so much code when I was overhauling the architecture of this app.
    #[allow(dead_code)]
    fn place_stone(&mut self, coordinate: Coordinate) -> Result<(), BoardError> {
        let board = self.board.clone();
        let new_board = board.place_stone(coordinate, self.current_player)?;

        if self.past_positions.contains(&new_board) {
            return Err(BoardError::Ko);
        }

        self.past_positions.push(self.board.clone());
        self.board = new_board;
        match self.current_player {
            Color::White => self.current_player = Color::Black,
            Color::Black => self.current_player = Color::White,
        };
        Ok(())
    }
}

/*
// To properly generate a tree, I need to know how deep to go. Then I can backtrace. Each node
// needs to have a depth. Given a tree, the depth of the node is just the distance from the root.
// This seems obvious, but I had to write it to discover how important that fact was.
//
// So, what is the maximum depth of the tree? Follow all paths and see how far I get in every case.
// I could do this by just generating an intermediate tree and numbering each level.
pub struct Tree<T> {
    nodes: Vec<Node<T>>,
}
*/

// https://llimllib.github.io/pymag-trees/
// I want to take advantage of the Wetherell Shannon algorithm, but I want some variations. In
// their diagram, they got a tree that looks like this.
//
// O
// |\
// O O
// |\ \ \
// O O O O
// |\  |\
// O O O O
//
// In the same circumstance, what I want is this:
//
// O--
// |  \
// O   O
// |\  |\
// O O O O
// |\
// O O
//
// In order to keep things from being overly smooshed, I want to ensure that if a branch overlaps
// with another branch, there is some extra drawing space. This might actually be similar to adding
// the principal that "A parent should be centered over its children".
//
// So, given a tree, I need to know how many children exist at each level. Then I build parents
// atop the children. At level 3, I have four children, and that happens to be the maximum width of
// the graph.
//
// A bottom-up traversal:
// - Figure out the number of nodes at each depth

pub struct DepthTree(nary_tree::Tree<SizeNode>);

impl Deref for DepthTree {
    type Target = nary_tree::Tree<SizeNode>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Default for DepthTree {
    fn default() -> Self {
        Self(nary_tree::Tree::new())
    }
}

#[derive(Debug)]
pub struct SizeNode {
    /// Use this to map back to the node in the original game tree. This way we know how to
    /// correspond from a node in the review tree back to there.
    pub game_node_id: nary_tree::NodeId,

    /// How deep into the tree is this node?
    depth: usize,

    /// How far from the leftmost margin is this node?
    width: usize,
}

impl SizeNode {
    pub fn position(&self) -> (usize, usize) {
        (self.depth, self.width)
    }
}

impl DepthTree {
    /*
    pub fn node(&self, idx: usize) -> &T {
        &self.nodes[idx].content
    }

    pub fn parent(&self, node: &Node<T>) -> Option<&Node<T>> {
        if let Some(parent_idx) = node.parent {
            self.nodes.get(parent_idx)
        } else {
            None
        }
    }

    // Add a node to the parent specified by parent_idx. Return the new index. This cannot be used
    // to add the root node, but the constructor should handle that, anyway.
    fn add_node(&mut self, parent_idx: usize, node: T) -> usize {
        let next_idx = self.nodes.len();
        let parent = &self.nodes[parent_idx];

        self.nodes.push(Node {
            id: next_idx,
            content: node,
            parent: Some(parent_idx),
            depth: parent.depth + 1,
            width: RefCell::new(None),
            children: vec![],
        });

        let parent = &mut self.nodes[parent_idx];
        parent.children.push(next_idx);
        next_idx
    }
    */

    pub fn max_depth(&self) -> usize {
        self.0
            .root()
            .unwrap()
            .traverse_pre_order()
            .fold(0, |max, node| {
                if node.data().depth > max {
                    node.data().depth
                } else {
                    max
                }
            })
    }

    // Since I know the width of a node, now I want to figure out its placement in the larger
    // scheme of things.
    //
    // One thought I have is that I could just develop a grid virtually and start placing nodes.
    // Whenever I notice a collision, I can just move the node over. But I'd like to see if I can
    // be a bit smarter than doing it as just a vec into which I place things, as though it's a
    // game board. So, given a game node, I want to figure out it's position along the X axis.
    //
    // Just having the node is greatly insufficient. I can get better results if I'm calculating
    // the position of its children.
    //
    // indent represents the indentation that should be applied to all children in this tree. It
    // amounts to the position of the parent node.
    //
    // When drawing nodes, I don't know how to persist the level of indent.

        // unimplemented!()
        /*
        let node = &self.nodes[idx];
        match node.parent {
            Some(parent_idx) => {
                let (_parent_row, parent_column) = self.position(parent_idx);
                let parent = &self.nodes[parent_idx];
                let sibling_width = parent
                    .children
                    .iter()
                    .take_while(|n| **n != node.id)
                    .fold(0, |acc, n| acc + self.width(*n));
                (node.depth, parent_column + sibling_width)
            }

            // Root nodes won't have a parent, so just put them in the first column
            None => (0, 0),
        }
        */

    /*
    // Given a node, do a postorder traversal to figure out the width of the node based on all of
    // its children. This is equivalent to the widest of all of its children at all depths.
    //
    // There are some collapse rules that I could take into account here, but that I haven't
    // figured out yet. If two nodes are side by side, and one of them has some wide children but
    // the other has no children, then they are effectively the same width. The second node only
    // needs to be moved out if it has children that would overlap the children of the first node.
    //
    // My algorithm right now is likely to generate unnecessarily wide trees in a complex game
    // review.
    fn width(&self, id: usize) -> usize {
        let node = &self.nodes[id];
        if let Some(width) = *node.width.borrow() {
            return width;
        }

        let width = node
            .children
            .iter()
            .fold(0, |acc, child| acc + self.width(*child));
        let width = if width == 0 { 1 } else { width };
        *node.width.borrow_mut() = Some(width);

        width
    }
    */

    pub fn bfs_iter(&self) -> BFSIter<'_, SizeNode> {
        let mut queue = VecDeque::new();
        queue.push_back(self.0.root().unwrap());
        BFSIter { queue }
    }
}

impl<'a> From<&'a GameTree> for DepthTree {
    fn from(tree: &'a GameTree) -> Self {
        // Like in the conversion from SGF to GameTree, I need to traverse the entire tree one node
        // at a time, keeping track of node ids as we go. I'm going to go with a depth-first
        // traversal. When generating each node, I think I want to generate all of the details of
        // the node as we go.
        let source_root_node = tree.root();
        match source_root_node {
            Some(source_root_node) => {
                // Do the real work
                // The id_map indexes from the source tree to the destination tree. Reverse
                // indexing is accomplished by looking at the node_id in a node in the destination
                // tree.
                let mut id_map: HashMap<nary_tree::NodeId, nary_tree::NodeId> = HashMap::new();
                let mut tree = nary_tree::Tree::new();

                let mut iter = source_root_node.traverse_pre_order();
                let _ = iter.next().unwrap(); // we already know that the first element to be
                                              // returned is the root node, and that the root node
                                              // already exists. Otherwise we wouldn't even be in
                                              // this branch.

                let dest_root_id = tree.set_root(SizeNode {
                    game_node_id: source_root_node.node_id(),
                    depth: 0,
                    width: 0,
                });

                id_map.insert(source_root_node.node_id(), dest_root_id);

                for source_node in iter {
                    let dest_parent_id = id_map
                        .get(&source_node.parent().unwrap().node_id())
                        .unwrap();

                    let mut dest_parent = tree.get_mut(*dest_parent_id).unwrap();

                    let new_depth_node = SizeNode {
                        game_node_id: source_node.node_id(),
                        depth: 1 + dest_parent.data().depth,
                        width: dest_parent.data().width,
                    };

                    let new_node_id = dest_parent.append(new_depth_node).node_id();

                    match tree
                        .get(new_node_id)
                        .unwrap()
                        .prev_sibling()
                        .map(|node| node.data().width)
                    {
                        None => {}
                        Some(previous_width) => {
                            let mut new_node = tree.get_mut(new_node_id).unwrap();
                            new_node.data().width = previous_width + 1;
                        }
                    }

                    /*
                    let new_node = tree.get_mut(*dest_parent_id).unwrap().append(new_depth_node);
                    let previous_node = new_node.prev_sibling();

                    match previous_node {
                        None => {}
                    }
                    */

                    /*
                    match dest_noderef.prev_sibling() {
                        None => {}
                        Some(mut node) => { dest_noderef.data().width = node.data().width + 1 }
                    }
                    */

                    id_map.insert(source_node.node_id(), new_node_id);
                }

                Self(tree)
            }
            None => Self::default(),
        }
    }
}

/*
impl<'a> From<&'a GameNode> for Tree<Uuid> {
    fn from(root: &'a GameNode) -> Self {
        fn add_subtree(tree: &mut Tree<Uuid>, parent_idx: usize, node: &GameNode) {
            let idx = tree.add_node(parent_idx, node.id());

            let children = match node {
                GameNode::MoveNode(node) => &node.children,
                GameNode::SetupNode(node) => &node.children,
            };

            for child in children {
                add_subtree(tree, idx, child);
            }
        }

        let mut tree = Tree::new(root.id());

        let children = match root {
            GameNode::MoveNode(node) => &node.children,
            GameNode::SetupNode(node) => &node.children,
        };

        for node in children {
            add_subtree(&mut tree, 0, node);
        }

        tree
    }
}
*/

pub struct BFSIter<'a, T> {
    queue: VecDeque<nary_tree::NodeRef<'a, T>>,
}

impl<'a, T> Iterator for BFSIter<'a, T> {
    type Item = NodeRef<'a, T>;

    fn next(&mut self) -> Option<Self::Item> {
        let retval = self.queue.pop_front();
        if let Some(ref retval) = retval {
            retval
                .children()
                .for_each(|noderef| self.queue.push_back(noderef));
        }
        retval
    }
}

#[cfg(test)]
mod test {
    use super::*;
    // use sgf::{GameRecord, GameTree, GameType, Move, MoveNode};
    use sgf::{GameNode, GameTree, Move, MoveNode};

    #[test]
    fn current_player_changes_after_move() {
        let mut state = GameState::default();
        assert_eq!(state.current_player, Color::Black);
        state.place_stone(Coordinate { column: 9, row: 9 }).unwrap();
        assert_eq!(state.current_player, Color::White);
    }

    #[test]
    fn current_player_remains_the_same_after_self_capture() {
        let mut state = GameState::default();
        state.board = Goban::from_coordinates(
            vec![
                (Coordinate { column: 17, row: 0 }, Color::White),
                (Coordinate { column: 17, row: 1 }, Color::White),
                (Coordinate { column: 18, row: 1 }, Color::White),
            ]
            .into_iter(),
        )
        .unwrap();
        state.current_player = Color::Black;

        assert_eq!(
            state.place_stone(Coordinate { column: 18, row: 0 }),
            Err(BoardError::SelfCapture)
        );
        assert_eq!(state.current_player, Color::Black);
    }

    #[test]
    fn ko_rules_are_enforced() {
        let mut state = GameState::default();
        state.board = Goban::from_coordinates(
            vec![
                (Coordinate { column: 7, row: 9 }, Color::White),
                (Coordinate { column: 8, row: 8 }, Color::White),
                (Coordinate { column: 8, row: 10 }, Color::White),
                (Coordinate { column: 9, row: 9 }, Color::White),
                (Coordinate { column: 10, row: 9 }, Color::Black),
                (Coordinate { column: 9, row: 8 }, Color::Black),
                (Coordinate { column: 9, row: 10 }, Color::Black),
            ]
            .into_iter(),
        )
        .unwrap();

        state.place_stone(Coordinate { column: 8, row: 9 }).unwrap();
        assert_eq!(
            state.place_stone(Coordinate { column: 9, row: 9 }),
            Err(BoardError::Ko)
        );
    }

    // A
    // B G   H
    // C     I
    // D E F
    fn branching_tree() -> GameTree {
        let mut game_tree = GameTree::default();
        let node_a = game_tree.set_root(GameNode::MoveNode(MoveNode::new(
            sgf::Color::Black,
            Move::Move("dp".to_owned()),
        )));

        let node_b = game_tree
            .get_mut(node_a)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let node_c = game_tree
            .get_mut(node_b)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let _node_d = game_tree
            .get_mut(node_c)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let _node_e = game_tree
            .get_mut(node_c)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let _node_f = game_tree
            .get_mut(node_c)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let _node_g = game_tree
            .get_mut(node_a)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let node_h = game_tree
            .get_mut(node_a)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        let _ = game_tree
            .get_mut(node_h)
            .unwrap()
            .append(GameNode::MoveNode(MoveNode::new(
                sgf::Color::Black,
                Move::Move("dp".to_owned()),
            )))
            .node_id();

        game_tree
    }

    #[test]
    fn it_can_calculate_depth_from_game_tree() {
        let game_tree = branching_tree();
        let tree = DepthTree::from(&game_tree);
        assert_eq!(
            game_tree.root().unwrap().traverse_pre_order().count(),
            tree.0.root().unwrap().traverse_pre_order().count()
        );
        assert_eq!(tree.max_depth(), 3);
    }

    #[test]
    fn it_calculates_horizontal_position_of_nodes() {
        let game_tree = branching_tree();
        let tree = DepthTree::from(&game_tree);

        let node_a = tree.root().unwrap();
        assert_eq!(node_a.data().position(), (0, 0));

        let node_b = node_a.first_child().unwrap();
        assert_eq!(node_b.data().position(), (1, 0));
        let node_g = node_b.next_sibling().unwrap();
        assert_eq!(node_g.data().position(), (1, 1));
        let node_h = node_g.next_sibling().unwrap();
        assert_eq!(node_h.data().position(), (1, 2));

        let node_c = node_b.first_child().unwrap();
        assert_eq!(node_c.data().position(), (2, 0));

        let node_d = node_c.first_child().unwrap();
        assert_eq!(node_d.data().position(), (3, 0));

        let node_i = node_h.first_child().unwrap();
        assert_eq!(node_i.data().position(), (2, 2));

        /*
        assert_eq!(tree.position(test_tree.node_c), (2, 0));
        assert_eq!(tree.position(test_tree.node_b), (1, 0));
        assert_eq!(tree.position(test_tree.node_a), (0, 0));
        assert_eq!(tree.position(test_tree.node_d), (3, 1));
        assert_eq!(tree.position(test_tree.node_e), (3, 2));
        assert_eq!(tree.position(test_tree.node_f), (1, 3));
        assert_eq!(tree.position(test_tree.node_g), (1, 4));
        */
    }
}
