#![feature(generic_const_exprs)]

mod system;

use std::fs::File;
use std::os::unix::fs::symlink;
use bevy::tasks::ParallelSlice;
use crate::system::spaces::square_grid::{Grid2D, Grid3D};
use itertools::Itertools;
use clap::Parser;
use crate::system::{GriddedPosition, Position};
use crate::system::model::HistoryLine;

fn box_count_2d(data: &Vec<Grid2D>, size: u32) -> usize {
    let n = data.len();
    let x_min = data
        .iter()
        .min_by(|Grid2D { x: x1, y: y1 }, Grid2D { x: x2, y: y2 }| x1.cmp(x2))
        .unwrap().x;

    let x_max = data
        .iter()
        .max_by(|Grid2D { x: x1, y: y1 }, Grid2D { x: x2, y: y2 }| x1.cmp(x2))
        .unwrap().x;

    let y_min = data
        .iter()
        .min_by(|Grid2D { x: x1, y: y1 }, Grid2D { x: x2, y: y2 }| y1.cmp(y2))
        .unwrap().y;

    let y_max = data
        .iter()
        .max_by(|Grid2D { x: x1, y: y1 }, Grid2D { x: x2, y: y2 }| y1.cmp(y2))
        .unwrap().y;

    let x_range = (x_max - x_min) as f64;
    let y_range = (y_max - y_min) as f64;

    let w: f64 = x_range / (size as f64);
    // let n_x = size;
    // let n_y = (y_range / w).ceil() as u32;

    let grid_points = data.iter()
        .map(|Grid2D { x, y }| [((x - x_min) as f64 / w) as u32, ((y - y_min) as f64 / w) as u32])
        .collect::<Vec<_>>();

    return grid_points.iter()
        .unique()
        .count();
}

fn box_count_3d(data: &Vec<Grid3D>, size: u32) -> usize {
    let n = data.len();

    let x_min = data
        .iter()
        .min_by(|Grid3D { x: x1, .. }, Grid3D { x: x2, .. }| x1.cmp(x2))
        .unwrap().x;

    let x_max = data
        .iter()
        .max_by(|Grid3D { x: x1, .. }, Grid3D { x: x2, .. }| x1.cmp(x2))
        .unwrap().x;

    let y_min = data
        .iter()
        .min_by(|Grid3D { y: v1, .. }, Grid3D { y: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let y_max = data
        .iter()
        .max_by(|Grid3D { y: v1, .. }, Grid3D { y: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let z_min = data
        .iter()
        .min_by(|Grid3D { z: v1, .. }, Grid3D { z: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let z_max = data
        .iter()
        .max_by(|Grid3D { z: v1, .. }, Grid3D { z: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let x_range = (x_max - x_min) as f64;
    let y_range = (y_max - y_min) as f64;
    let z_range = (z_max - z_min) as f64;

    let w: f64 = x_range / (size as f64);

    let grid_points = data.iter()
        .map(|Grid3D { x, y, z }| [
            ((x - x_min) as f64 / w) as u32,
            ((y - y_min) as f64 / w) as u32,
            ((z - z_min) as f64 / w) as u32,
        ])
        .collect::<Vec<_>>();

    return grid_points.iter()
        .unique()
        .count();
}

fn box_count_nd<const N: usize, P: Position>(data: &Vec<P>, size: u32) -> usize {
    let n = data.len();
    let pp = data.iter().map(|op| op.to_cartesian()).collect::<Vec<P::Cartesian>>();

    let x_min = data
        .iter()
        .min_by(|Grid3D { x: x1, .. }, Grid3D { x: x2, .. }| x1.cmp(x2))
        .unwrap().x;

    let x_max = data
        .iter()
        .max_by(|Grid3D { x: x1, .. }, Grid3D { x: x2, .. }| x1.cmp(x2))
        .unwrap().x;

    let y_min = data
        .iter()
        .min_by(|Grid3D { y: v1, .. }, Grid3D { y: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let y_max = data
        .iter()
        .max_by(|Grid3D { y: v1, .. }, Grid3D { y: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let z_min = data
        .iter()
        .min_by(|Grid3D { z: v1, .. }, Grid3D { z: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let z_max = data
        .iter()
        .max_by(|Grid3D { z: v1, .. }, Grid3D { z: v2, .. }| v1.cmp(v2))
        .unwrap().y;

    let x_range = (x_max - x_min) as f64;
    let y_range = (y_max - y_min) as f64;
    let z_range = (z_max - z_min) as f64;

    let w: f64 = x_range / (size as f64);

    let grid_points = data.iter()
        .map(|Grid3D { x, y, z }| [
            ((x - x_min) as f64 / w) as u32,
            ((y - y_min) as f64 / w) as u32,
            ((z - z_min) as f64 / w) as u32,
        ])
        .collect::<Vec<_>>();

    return grid_points.iter()
        .unique()
        .count();
}

#[derive(Parser)]
struct FDArgs {
    path: std::path::PathBuf,
}

fn main() {
    let args = FDArgs::parse();
    let lines: Vec<HistoryLine<Grid3D>> = serde_json::from_reader(File::open(args.path).unwrap())
        .expect("Failed to read json");

    let a = lines.iter()
        .map(|l| l.position.clone())
        .collect::<Vec<_>>();

    for size in 1..250 {
        println!("[{}, {:?}],", size, box_count_3d(&a, size));
    }
}
