use crate::logic::*;
use rand::seq::SliceRandom;
use rand::{Rng, SeedableRng};
use rand_xoshiro::SplitMix64;
use xxhash_rust::xxh3::xxh3_64;

impl Board {
    pub fn new(seed: &str) -> Board {
        let mut rng = SplitMix64::seed_from_u64(xxh3_64(seed.as_bytes()));
        let hexes = {
            let mut arr = [[None; 7]; 7];
            for h in Board::hexes(&mut rng) {
                arr[h.position.0 as usize][h.position.1 as usize] = Some(h);
            }
            arr
        };
        let (edges, vertices) = {
            let mut es = [[None; 15]; 15];
            let mut vs = [[None; 15]; 15];
            for hex in hexes.iter().flatten() {
                match hex {
                    Some(Hex {
                        hextype: HexType::Resource | HexType::Desert,
                        position: p,
                        ..
                    }) => {
                        let mut d = Position::new(1, 0);
                        for _ in 0..6 {
                            let e = p.edge(&d);
                            if es[e.0 as usize][e.1 as usize].is_none() {
                                es[e.0 as usize][e.1 as usize] = Some(Edge {
                                    position: e,
                                    road: None,
                                });
                            }
                            if vs[e.0 as usize][e.1 as usize].is_none() {
                                vs[e.0 as usize][e.1 as usize] = Some(Vertex {
                                    position: e.vertex(&d),
                                    settlement: None,
                                    city: None,
                                });
                            }
                            d.rotate();
                        }
                    }
                    _ => (),
                }
            }
            (es, vs)
        };

        let board = Board {
            hexes,
            edges,
            vertices,
            robber: hexes
                .iter()
                .flatten()
                .find(|h| h.is_some() && h.unwrap().hextype == HexType::Desert)
                .unwrap()
                .unwrap()
                .position,
        };
        board
    }

    fn hexes<R: Rng>(mut rng: &mut R) -> Vec<Hex> {
        // get random direction
        let direction = {
            let mut d = Position::new(1, 0);
            for _ in 0..rng.gen_range(0..6) {
                d.rotate();
            }
            d
        };
        let ps = Board::spiral_order(&direction);
        let mut hexes = Vec::new();

        // randomize port resources
        let mut port_resources = {
            let mut v = vec![
                None,
                None,
                None,
                None,
                Some(Resource::Wood),
                Some(Resource::Brick),
                Some(Resource::Sheep),
                Some(Resource::Wheat),
                Some(Resource::Ore),
            ];
            v.shuffle(&mut rng);
            v
        };
        let mut orientation = direction;
        orientation.rotate().rotate();
        for (i, p) in ps[0..18].iter().enumerate() {
            if i % 2 == 0 {
                //is a port
                if i % 3 != 1 {
                    orientation.rotate();
                }
                hexes.push(Hex {
                    hextype: HexType::Port,
                    resource: port_resources.pop().unwrap(),
                    position: *p,
                    number: None,
                    orientation: Some(orientation),
                });
            } else {
                hexes.push(Hex {
                    hextype: HexType::Ocean,
                    resource: None,
                    position: *p,
                    number: None,
                    orientation: None,
                });
            }
        }

        let mut numbers = {
            let mut v = vec![5, 2, 6, 3, 8, 10, 9, 12, 11, 4, 8, 10, 9, 4, 5, 6, 3, 11];
            v.reverse();
            v
        };
        let mut hex_resources = {
            let mut h = Vec::new();
            for _ in 0..4 {
                h.push(Resource::Wood);
                h.push(Resource::Sheep);
                h.push(Resource::Wheat);
            }
            for _ in 0..3 {
                h.push(Resource::Brick);
                h.push(Resource::Ore);
            }
            h.shuffle(&mut rng);
            h
        };
        let desert_pos = rng.gen_range(0..18);
        for (i, p) in ps[18..].iter().enumerate() {
            if i == desert_pos {
                hexes.push(Hex {
                    hextype: HexType::Desert,
                    resource: None,
                    position: *p,
                    number: None,
                    orientation: None,
                });
            } else {
                hexes.push(Hex {
                    hextype: HexType::Resource,
                    resource: Some(hex_resources.pop().unwrap()),
                    position: *p,
                    number: Some(numbers.pop().unwrap()),
                    orientation: None,
                });
            };
        }
        hexes
    }

    fn spiral_order(direction: &Position) -> Vec<Position> {
        let mut ps = Vec::new();
        for r in (1..=3).rev() {
            ps.extend(&Board::ring(&direction, r));
        }
        ps.push(Position::new(3, 3));
        ps
    }

    fn ring(direction: &Position, radius: u32) -> Vec<Position> {
        let mut ps = Vec::new();
        let mut current = Position::new(3, 3);
        for _ in 0..radius {
            current.add(direction);
        }
        let mut d = *direction;
        d.rotate().rotate();
        for _ in 0..6 {
            for _ in 0..radius {
                ps.push(current);
                current.add(&d);
            }
            d.rotate();
        }
        ps
    }
}