//! The 10 layout presets (DOCS/MAIN.md §8.2). A preset maps a list of surface
//! ids (one per slot, in order) to a LayoutNode. `slot_count` says how many
//! panels the preset needs.
use spacesh_proto::ids::SurfaceId;
use spacesh_proto::layout::{LayoutNode, Orient};

/// Known preset ids and their panel counts.
pub fn slot_count(preset_id: &str) -> Option<usize> {
    Some(match preset_id {
        "1" => 1,
        "2lr" => 2,   // 2↔
        "2tb" => 2,   // 2↕
        "2+1" => 3,
        "1+2" => 3,
        "3" => 3,
        "2x2" => 4,
        "4" => 4,     // single row of 4
        "2x3" => 6,
        "2x4" => 8,
        _ => return None,
    })
}

fn leaf(id: &SurfaceId) -> LayoutNode { LayoutNode::leaf(id.clone()) }
fn even(n: usize) -> Vec<f32> { vec![1.0 / n as f32; n] }

fn row(ids: &[SurfaceId]) -> LayoutNode {
    LayoutNode::Split { orient: Orient::H, ratios: even(ids.len()), children: ids.iter().map(leaf).collect() }
}
fn col(children: Vec<LayoutNode>) -> LayoutNode {
    LayoutNode::Split { orient: Orient::V, ratios: even(children.len()), children }
}
fn rown(children: Vec<LayoutNode>) -> LayoutNode {
    LayoutNode::Split { orient: Orient::H, ratios: even(children.len()), children }
}

/// Build the preset tree from exactly `slot_count(preset_id)` ids.
/// Returns None for an unknown id or wrong id count.
pub fn build(preset_id: &str, ids: &[SurfaceId]) -> Option<LayoutNode> {
    if slot_count(preset_id)? != ids.len() {
        return None;
    }
    Some(match preset_id {
        "1" => leaf(&ids[0]),
        "2lr" => row(&ids),
        "2tb" => col(vec![leaf(&ids[0]), leaf(&ids[1])]),
        // left big column over... 2 stacked on the right.
        "2+1" => rown(vec![leaf(&ids[0]), col(vec![leaf(&ids[1]), leaf(&ids[2])])]),
        // one big on the left, 2 stacked on the right (mirror naming kept simple).
        "1+2" => rown(vec![col(vec![leaf(&ids[0]), leaf(&ids[1])]), leaf(&ids[2])]),
        "3" => row(&ids),
        "2x2" => col(vec![row(&ids[0..2]), row(&ids[2..4])]),
        "4" => row(&ids),
        "2x3" => col(vec![row(&ids[0..3]), row(&ids[3..6])]),
        "2x4" => col(vec![row(&ids[0..4]), row(&ids[4..8])]),
        _ => return None,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ops::leaves;
    fn ids(n: usize) -> Vec<SurfaceId> { (0..n).map(|i| SurfaceId(format!("s_{i}"))).collect() }

    #[test]
    fn all_presets_have_counts() {
        for p in ["1","2lr","2tb","2+1","1+2","3","2x2","4","2x3","2x4"] {
            assert!(slot_count(p).is_some(), "missing count for {p}");
        }
        assert!(slot_count("nope").is_none());
    }

    #[test]
    fn build_uses_all_ids_in_order() {
        for p in ["1","2lr","2tb","2+1","1+2","3","2x2","4","2x3","2x4"] {
            let n = slot_count(p).unwrap();
            let tree = build(p, &ids(n)).unwrap();
            assert_eq!(leaves(&tree), ids(n), "preset {p} must place all ids in order");
        }
    }

    #[test]
    fn build_rejects_wrong_id_count() {
        assert!(build("2x2", &ids(3)).is_none());
        assert!(build("bogus", &ids(1)).is_none());
    }

    #[test]
    fn grid_2x2_is_two_rows() {
        let tree = build("2x2", &ids(4)).unwrap();
        match tree {
            LayoutNode::Split { orient: Orient::V, children, .. } => {
                assert_eq!(children.len(), 2);
                for r in &children {
                    matches!(r, LayoutNode::Split { orient: Orient::H, .. });
                }
            }
            _ => panic!("2x2 should be a vertical split of two horizontal rows"),
        }
    }
}
