use super::color_mapper::{BoundaryNorm, ColorMapper};
use femtovg::{
    renderer::OpenGl, Canvas, ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget,
};
use geo_types::LineString;
use ndarray::ArrayView2;
use num_traits::{Num, NumOps};
use std::{fmt::Debug, marker::PhantomData};

use super::super::renders::DataRenderer;
use crate::{
    data::Radar2d,
    render::{cms::CMS, LayerImpl, Render, Target},
    utils::meshgrid,
};

#[derive(Debug)]
pub struct GridFieldRenderer<CMAP, T>
where
    T: NumOps + PartialOrd,
    CMAP: ColorMapper<T>,
{
    cmap: CMAP,
    value_phantom: PhantomData<T>,
}

impl<T: NumOps + PartialOrd + Copy, CMAP: ColorMapper<T>> GridFieldRenderer<CMAP, T> {
    pub fn new(cmap: CMAP) -> Self {
        Self {
            cmap,
            value_phantom: PhantomData,
        }
    }
    fn draw_2d(
        &self,
        canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
        cms: &CMS,
        data: ArrayView2<T>,
        dims: (ArrayView2<f64>, ArrayView2<f64>),
        window_size: (f32, f32),
        fill_value: T,
    ) {
        let shape = data.shape();
        let (rows, cols) = (shape[0], shape[1]);
        let (dim1, dim2) = dims;

        let d1_s = dim1[[0, 0]];
        let d1_e = dim1[[0, cols - 1]];

        let d2_s = dim2[[0, 0]];
        let d2_e = dim2[[rows - 1, 0]];

        for r in 0..rows - 1 {
            for c in 0..cols - 1 {
                let lb_lat = dim2[[r, c]];
                let lb_lon = dim1[[r, c]];

                let rt_lat = dim2[[r + 1, c + 1]];
                let rt_lon = dim1[[r + 1, c + 1]];
                let cell: LineString = vec![
                    (lb_lon, lb_lat),
                    (rt_lon + 0.001, lb_lat),
                    (rt_lon + 0.001, rt_lat),
                    (lb_lon, rt_lat + 0.001),
                    (lb_lon, lb_lat + 0.001),
                ]
                .into();

                let v = &data[[r, c]];
                let mapped_color = self.cmap.map_value_to_color(*v, fill_value);

                if None == mapped_color {
                    continue;
                }

                let mapped_ring = cms.ring_map(&cell).unwrap();

                let mut path = Path::new();
                let mut points = mapped_ring.points();
                let first_point = points.next().unwrap();
                path.move_to(first_point.x(), first_point.y());

                for point in points {
                    path.line_to(point.x(), point.y());
                }
                path.close();
                canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
            }
        }
    }
}

impl<T, CMAP> DataRenderer for GridFieldRenderer<CMAP, T>
where
    T: Num + NumOps + PartialOrd + Copy + Clone,
    CMAP: ColorMapper<T>,
{
    type Data = Radar2d<T>;

    fn render(
        &self,
        canvas: &mut Canvas<OpenGl>,
        cms: &CMS,
        data: &Self::Data,
        size: (f32, f32),
    ) -> Target {
        let (w, h) = size;
    }

    // fn render(&self, canvas: &mut Canvas<OpenGl>, cms: &CMS, data: &Self::Data) -> Target {
    //     let _data = data.data.view();
    //     let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view());
    //     self.draw_2d(
    //         canvas,
    //         cms,
    //         _data,
    //         (_dim1.view(), _dim2.view()),
    //         (3000.0, 3000.0),
    //         data.fill_value,
    //     );

    //     let d1_start = (data.dim1.view()).first().unwrap().clone();
    //     let d1_end = (data.dim1.view()).last().unwrap().clone();

    //     let d2_start = data.dim2.view().first().unwrap().clone();
    //     let d2_end = data.dim2.view().last().unwrap().clone();

    //     Target::new(
    //         new_img,
    //         3000f32,
    //         3000f32,
    //         ((d1_start, d1_end).into(), (d2_start, d2_end).into()),
    //     )
    // }
}

#[derive(Debug)]
pub struct GridLayerImpl<CMAP, T>
where
    T: Num + NumOps + PartialOrd + Copy + Clone + Send + Sync + Debug,
    CMAP: ColorMapper<T>,
{
    renderer: GridFieldRenderer<CMAP, T>,
    data: Radar2d<T>,
}

pub type DbzGridLayerImpl = GridLayerImpl<BoundaryNorm<i8>, i8>;

impl<CMAP, T> GridLayerImpl<CMAP, T>
where
    T: Num + NumOps + PartialOrd + Copy + Clone + Send + Sync + Debug,
    CMAP: ColorMapper<T>,
{
    pub fn new(renderer: GridFieldRenderer<CMAP, T>, data: Radar2d<T>) -> Self {
        Self { renderer, data }
    }
}

impl<CMAP, T> LayerImpl for GridLayerImpl<CMAP, T>
where
    T: Num + NumOps + PartialOrd + Copy + Clone + Debug + Send + Sync,
    CMAP: ColorMapper<T> + Debug,
{
    fn draw(
        &self,
        canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
        cms: &crate::render::cms::CMS,
    ) -> Option<Target> {
        let new_img = canvas
            .create_image_empty(3000, 3000, Rgba8, ImageFlags::empty())
            .expect("Can't Create Image");

        if let Ok(_) = canvas.image_size(new_img) {
            canvas.reset();
            canvas.set_render_target(RenderTarget::Image(new_img));
        }

        canvas.set_render_target(RenderTarget::Screen);

        Some(self.renderer.render(Render::new(canvas, cms), &self.data))
    }
}
