use std::cmp::max;
use std::cmp::min;

#[derive(Debug)]
pub struct HuffmanAlphabet {
    pub length: usize,
    pub frequencies: Vec<u64>,
}

#[derive(Debug)]
struct HuffmanTreeNode {
    frequency: u64,
    left: Option<Box<HuffmanTreeNode>>,
    right: Option<Box<HuffmanTreeNode>>,
}

fn max_depth(tree: &HuffmanTreeNode) -> usize {
    let mut depth_left = 0;
    if let Some(node_left) = &tree.left {
        depth_left = 1 + max_depth(&node_left);
    }

    let mut depth_right = 0;
    if let Some(node_right) = &tree.right {
        depth_right = 1 + max_depth(&node_right);
    }

    max(depth_left, depth_right)
}

fn min_depth(tree: &HuffmanTreeNode) -> usize {
    let mut depth_left = 0;
    if let Some(node_left) = &tree.left {
        depth_left = 1 + min_depth(&node_left);
    }

    let mut depth_right = 0;
    if let Some(node_right) = &tree.right {
        depth_right = 1 + min_depth(&node_right);
    }

    min(depth_left, depth_right)
}

pub fn build_huffman_tree(h: &HuffmanAlphabet) -> (usize, usize) {
    let mut nodes: Vec<HuffmanTreeNode> = Vec::new();
    for f in &h.frequencies {
        let n = HuffmanTreeNode {
            frequency: *f,
            left: None,
            right: None,
        };
        nodes.push(n);
    }
    nodes.sort_by(|a, b| b.frequency.cmp(&a.frequency));
    while nodes.len() > 1 {
        let a = nodes.pop().unwrap();
        let b = nodes.pop().unwrap();
        let n = HuffmanTreeNode {
            frequency: a.frequency + b.frequency,
            left: Some(Box::new(a)),
            right: Some(Box::new(b)),
        };
        nodes.push(n);
        nodes.sort_by(|a, b| b.frequency.cmp(&a.frequency));
    }
    let tree = nodes.pop().unwrap();
    (min_depth(&tree), max_depth(&tree))
}
