#![feature(generic_const_exprs)]
#![feature(let_chains)]

use std::fs::File;
use std::path::PathBuf;
use anyhow::Context;
use crate::cli::cli::OutputFormat;
use crate::system::model::HistoryLine;
use crate::system::spaces::square_grid::Grid2D;
use clap::Parser;
use serde::de::DeserializeOwned;
use serde::Deserialize;
use svg::Node;
use svg::node::element::Rectangle;
use crate::system::Position;
use crate::system::spaces::hexagonal::HexPosition;

#[derive(Debug, Parser)]
struct Args {
    format: OutputFormat,
    path: PathBuf,
    output: PathBuf,
}

trait ToSvg {
    fn to_svg(&self, size: i32) -> Box<dyn Node>;
}

impl ToSvg for Grid2D {
    fn to_svg(&self, size: i32) -> Box<dyn Node> {
        Box::new(Rectangle::new()
            .set("fill", "rgb(0, 0, 0)")
            .set("width", size)
            .set("height", size)
            .set("x", self.x * size)
            .set("y", self.y * size))
    }
}

impl ToSvg for HexPosition {
    fn to_svg(&self, size: i32) -> Box<dyn Node> {
        let points = [
            [25.045, 128.0], [256.0, 0.0], [486.955, 128.0], [486.955, 384.0], [256.0, 512.0], [25.045, 384.0]
        ];

        let size = size as f32;

        let b = points.map(|x| [
            (x[0] / 512.0) * (size),
            (x[1] / 512.0) * (size)]
        );

        let c = b.map(|p| format!("{},{}", p[0], p[1])).join(" ");

        let [x, y] = self.to_cartesian();
        Box::new(Rectangle::new()
            .set("fill", "rgb(0, 0, 0)")
            .set("x", x * size)
            .set("y", y * size))
    }
}

fn read_json<T: Position>(args: &Args) -> Vec<T> where T: DeserializeOwned {
    serde_json::from_reader::<_, Vec<HistoryLine<T>>>(File::open(&args.path).expect("Failed to open file"))
        .expect("Failed to read json")
        .iter()
        .map(|l| (l.position.clone()))
        .collect::<Vec<_>>()
}

fn read_csv<T: Position>(args: &Args) -> Vec<T> where T: DeserializeOwned {
    csv::Reader::from_path(&args.path).expect("Failed to read positions csv").deserialize::<T>()
        .collect::<Result<Vec<T>, _>>()
        .unwrap()
}

fn main() {
    let args = Args::parse();
    dbg!(&args);

    let positions: Vec<Grid2D> = match args.format {
        OutputFormat::FullDataJson => read_json::<Grid2D>(&args),
        OutputFormat::Positions => read_csv::<Grid2D>(&args),
    };

    let size: i32 = 800;
    let max_x = positions.iter().max_by(|a, b| a.x.abs().cmp(&b.x.abs())).unwrap().x.abs();
    let max_y = positions.iter().max_by(|a, b| a.y.abs().cmp(&b.y.abs())).unwrap().y.abs();
    let max_size = max_x.max(max_y) * size;

    let mut svg = svg::Document::new()
        .set("width", max_size * size)
        .set("height", max_size * size)
        .set("viewBox", format!("{} {} {} {}", -max_size, -max_size, max_size * 2, max_size * 2));

    svg.append(Rectangle::new()
        .set("fill", "white")
        .set("width", max_size * 2)
        .set("height", max_size * 2)
        .set("x", -max_size)
        .set("y", -max_size)
    );

    for position in positions {
        svg.append(position.to_svg(size));
    }

    svg::write(File::create(args.output).unwrap(), &svg).unwrap();
}
