use std::ops::Range;
use cgmath::{Vector3, InnerSpace, Zero};
use noise::{BasicMulti, Seedable, NoiseModule};
use spherical_voronoi as sv;
use rand::{thread_rng, Rng, Rand};
use color::Color;
use ideal::{IdVec, IdsIter};
use settings;

#[derive(Copy, Clone, PartialEq)]
#[derive(Serialize, Deserialize)]
pub enum CornerKind {
    Water,
    Land,
    Coast,
    River,
}

#[derive(Clone)]
#[derive(Serialize, Deserialize)]
pub struct CornerData {
    regions: Vec<Region>,
    position: Vector3<f64>,
    kind: CornerKind,
    elevation: f64,
}
create_id!(Corner);

#[derive(Copy, Clone, PartialEq)]
#[derive(Serialize, Deserialize)]
pub enum BorderKind {
    River,
    Coast,
    None,
}

#[derive(Clone)]
#[derive(Serialize, Deserialize)]
pub struct BorderData {
    kind: BorderKind,
    corners: (Corner, Corner),
    regions: (Region, Region),
}
create_id!(Border);

#[derive(Clone, PartialEq, Debug)]
#[derive(Serialize, Deserialize)]
pub struct BiomeData {
    name: String,
    color: Color,
    is_land: bool,
}
create_id!(Biome);

#[derive(Clone)]
#[derive(Serialize, Deserialize)]
pub struct RegionData {
    biome: Biome,
    corners: Vec<Corner>,
    borders: Vec<Border>,
    center: Vector3<f64>,
    elevation: f64,
}
create_id!(Region);

#[derive(Serialize, Deserialize)]
pub struct Map {
    biomes: IdVec<Biome, BiomeData>,
    corners: IdVec<Corner, CornerData>,
    borders: IdVec<Border, BorderData>,
    regions: IdVec<Region, RegionData>,
    borders: &'a IdVec<Border, BorderData>,
    region: Region,
    inner: ::std::slice::Iter<'b, Border>,
}

impl<'a, 'b> ::std::iter::Iterator for Neighbors<'a, 'b> {
    type Item = Region;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(border) = self.inner.next() {
            let (region0, region1) = self.borders[*border].regions;
            if region0 == self.region {
                Some(region1)
            } else {
                Some(region0)
            }
        } else {
            None
        }
    }
}

impl Map {
    pub fn regions(&self) -> IdsIter<Region> {
        self.regions.ids()
    }

    pub fn borders(&self) -> IdsIter<Border> {
        self.borders.ids()
    }

    pub fn corners(&self) -> IdsIter<Corner> {
        self.corners.ids()
    }

    pub fn neighbors(&self, region: Region) -> Neighbors {
        Neighbors {
            borders: &self.borders,
            region: region,
            inner: self.regions[region].borders.iter(),
        }
    }

    pub fn border_corners(&self, border: Border) -> (Corner, Corner) {
        self.borders[border].corners
    }

    pub fn border_regions(&self, border: Border) -> (Region, Region) {
        self.borders[border].regions
    }

    pub fn border_kind(&self, border: Border) -> BorderKind {
        self.borders[border].kind
    }

    pub fn biome(&self, region: Region) -> Biome {
        self.regions[region].biome
    }

    pub fn is_land(&self, region: Region) -> bool {
        self.biomes[self.biome(region)].is_land
    }

    pub fn biome_color(&self, biome: Biome) -> Color {
        self.biomes[biome].color
    }

    pub fn region_center(&self, region: Region) -> Vector3<f64> {
        self.regions[region].center
    }

    pub fn region_borders(&self, region: Region) -> &[Border] {
        &self.regions[region].borders
    }

    pub fn region_elevation(&self, region: Region) -> f64 {
        self.regions[region].elevation
    }

    pub fn corner_elevation(&self, corner: Corner) -> f64 {
        self.corners[corner].elevation
    }

    pub fn corner_position(&self, corner: Corner) -> Vector3<f64> {
        self.corners[corner].position
    }

    pub fn corner_regions(&self, corner: Corner) -> &[Region] {
        &self.corners[corner].regions
    }
}

#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Generator {
    size: usize,
    relaxations: usize,
    sea_level: f64,
    elevation_equator_bias: f64,
    temperature_equator_bias: f64,
    moisture_equator_bias: f64,
    noise: settings::NoiseSettings,
    biomes: Vec<BiomeData>,
    ocean: Biome,
    sea: Biome,
    land_biomes: Vec<Vec<Biome>>,
}


impl Generator {
    pub fn generate(&self) -> Map {
        let mut visitor = Visitor::default();
        sv::build_relaxed(&generate_points(self.size), &mut visitor, self.relaxations);
        let Visitor { mut corners, mut borders, mut regions } = visitor;
        for region in regions.ids() {
            regions[region].center = regions[region].corners
                .iter()
                .fold(Vector3::zero(), |acc, &corner| acc + corners[corner].position)
                .normalize()
                .into();
        }
        let mut biomes = IdVec::default();
        for biome in self.biomes.iter() {
            biomes.push(biome.clone());
        }
        let mut map = Map {
            biomes: biomes,
            corners: corners,
            borders: borders,
            regions: regions,
        };
        self.generate_biomes(&mut map);
        //self.generate_rivers(&mut map);
        map
    }

    fn generate_biomes(&self, map: &mut Map) {
        let elevations = self.generate_noise(map, self.elevation_equator_bias);
        let temperatures = self.generate_noise(map, self.temperature_equator_bias);
        let moistures = self.generate_noise(map, self.moisture_equator_bias);
        for region in map.regions() {
            let elevation = elevations[region];
            map.regions[region].elevation = elevation;
            if elevation < self.sea_level {
                map.regions[region].biome = self.ocean;
            } else {
                map.regions[region].biome = self.get_biome(temperatures[region], moistures[region]);
            }
        }
        for region in map.regions() {
            if map.biome(region) == self.ocean &&
               map.neighbors(region).any(|neighbor| map.is_land(neighbor)) {
                map.regions[region].biome = self.sea;
            }
        }
        for corner in map.corners() {
            let elevation = {
                let regions = map.corner_regions(corner);
                let len = regions.len() as f64;
                regions.iter().map(|&region| map.region_elevation(region)).sum::<f64>() / len
            };
            map.corners[corner].elevation = elevation;
        }
    }

    fn get_biome(&self, temperature: f64, moisture: f64) -> Biome {
        let biomes = get_element(self.land_biomes.as_slice(), temperature);
        *get_element(biomes, moisture)
    }

    fn generate_noise(&self, map: &Map, equator_bias: f64) -> IdVec<Region, f64> {
        let noise = BasicMulti::new().set_seed(thread_rng().gen());
        let mut pairs = Vec::new();
        for region in map.regions() {
            let center = map.region_center(region);
            pairs.push((region, noise.get([center.x, center.y, center.z]) + equator_bias * (1.0 - center[2].abs())));
        }
        pairs.sort_by(|left, right| left.1.partial_cmp(&right.1).unwrap());
        let mut values = IdVec::from(vec![0.0; pairs.len()]);
        for (index, &(region, _)) in pairs.iter().enumerate() {
            values[region] = index as f64 / pairs.len() as f64;
        }
        values
    }

    // fn generate_rivers(&self, map: &mut Map) {
    //     for vertex in map.diagram.vertices() {
    //         let mut elevation = map.vertex_elevation(vertex);
    //         if elevation > 0.8 {
    //             let mut vertex = vertex;
    //             while map.vertex_kind(vertex) == VertexKind::Land {
    //                 map.vertices[vertex.index()].kind = VertexKind::River;
    //                 let mut edge = None;
    //                 let mut next = vertex;
    //                 // find the lowest adjacent corner
    //                 for &e in map.diagram.vertex_edges(vertex) {
    //                     let v = map.diagram.other_edge_vertex(e, vertex);
    //                     let el = map.vertex_elevation(v);
    //                     if el < elevation {
    //                         elevation = el;
    //                         edge = Some(e);
    //                         next = v;
    //                     }
    //                 }
    //                 if let Some(edge) = edge {
    //                     map.edges[edge.index()].kind = EdgeKind::River;
    //                     vertex = next;
    //                 } else {
    //                     break;
    //                 }
    //             }
    //         }
    //     }
    // }


}

#[derive(Default)]
struct Visitor {
    corners: IdVec<Corner, CornerData>,
    borders: IdVec<Border, BorderData>,
    regions: IdVec<Region, RegionData>,
}

impl Visitor {
    fn common_regions(&self, corner0: Corner, corner1: Corner) -> (Region, Region) {
        let mut regions = (Region::invalid(), Region::invalid());
        for &region0 in self.corners[corner0].regions.iter() {
            for &region1 in self.corners[corner1].regions.iter() {
                if region0 == region1 {
                    if regions.0.is_invalid() {
                        regions.0 = region0;
                    } else {
                        regions.1 = region0;
                    }
                }
            }
        }
        regions
    }
}

impl sv::Visitor for Visitor {
    fn vertex(&mut self, position: Vector3<f64>, cells: [usize; 3]) {
        let region0 = Region(cells[0]);
        let region1 = Region(cells[1]);
        let region2 = Region(cells[2]);
        let corner = self.corners.push(CornerData {
            regions: vec![region0, region1, region2],
            position: position,
            elevation: 0.0,
            kind: CornerKind::Water,
        });
        self.regions[region0].corners.push(corner);
        self.regions[region1].corners.push(corner);
        self.regions[region2].corners.push(corner);
    }

    fn edge(&mut self, vertices: [usize; 2]) {
        let corner0 = Corner(vertices[0]);
        let corner1 = Corner(vertices[1]);
        let (region0, region1) = self.common_regions(corner0, corner1);
        let border = self.borders.push(BorderData {
            kind: BorderKind::None,
            corners: (corner0, corner1),
            regions: (region0, region1),
        });
        self.regions[region0].borders.push(border);
        self.regions[region1].borders.push(border);
    }

    fn cell(&mut self) {
        self.regions.push(RegionData {
            corners: Vec::new(),
            borders: Vec::new(),
            center: Vector3::zero(),
            elevation: 0.0,
            biome: Biome(0),
        });
    }
}

fn generate_points(count: usize) -> Vec<Vector3<f64>> {
    let mut points = Vec::with_capacity(count);
    let mut created = 0;
    while created < count {
        let x1 = thread_rng().gen_range(-1.0f64, 1.0);
        let x2 = thread_rng().gen_range(-1.0f64, 1.0);
        let norm2 = x1 * x1 + x2 * x2;
        if norm2 < 1.0 {
            created += 1;
            let x = 2.0 * x1 * (1.0 - norm2).sqrt();
            let y = 2.0 * x2 * (1.0 - norm2).sqrt();
            let z = 1.0 - 2.0 * norm2;
            points.push(Vector3::new(x, y, z));
        }
    }
    points
}

fn get_element<T>(items: &[T], index: f64) -> &T {
    &items[((items.len() as f64) * index).floor() as usize]
}
}

pub struct Neighbors<'a, 'b> {