use crate::{coords::Range, render::Render};
use femtovg::{renderer::OpenGl, Canvas, ImageId};
use std::{
    cell::{Ref, RefCell},
    fmt::Debug, sync::Arc,
};

#[derive(Clone)]
pub struct Layer {
    pub visiable: bool,
    target: RefCell<Option<Target>>,
    imp: RefCell<Option<Arc<dyn LayerImpl>>>,
    draw: Arc<dyn Fn(&Self, &mut Canvas<OpenGl>, &Render, (f32, f32))>,
}


impl Debug for Layer {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Layer")
            .field("visiable", &self.visiable)
            .field("target", &self.target)
            .field("imp", &self.imp)
            .finish()
    }
}

pub trait LayerImpl: Debug {
    fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render) -> Option<Target>;
}

impl Layer {
    pub fn new<
        F: 'static + Fn(&Self, &mut Canvas<OpenGl>, &Render, (f32, f32)),
        IMP: LayerImpl + 'static,
    >(
        visiable: bool,
        draw: F,
        imp: Option<IMP>,
    ) -> Self {
        Layer {
            visiable,
            target: RefCell::new(None),
            draw: Arc::new(draw),
            imp: RefCell::new(imp.map(|x| Arc::new(x) as Arc<dyn LayerImpl>)),
        }
    }

    pub fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render, window_size: (f32, f32)) {
        if self.visiable {
            (self.draw)(self, canvas, render, window_size);
        }
    }

    pub fn set_render_target(&self, target: Target) {
        *self.target.borrow_mut() = Some(target);
    }

    pub fn render_target(&self) -> Option<Target> {
        self.target.borrow().clone()
    }

    pub fn get_imp(&self) -> Ref<Option<Arc<dyn LayerImpl>>> {
        let im = self.imp.borrow();
        im
    }
}

#[derive(Clone, Copy, Debug)]
pub struct Target {
    pub target: ImageId,
    width: f32,
    height: f32,
    bounds: (Range, Range),
}

impl Target {
    pub fn new(target: ImageId, width: f32, height: f32, bounds: (Range, Range)) -> Self {
        Self {
            target,
            width,
            height,
            bounds,
        }
    }

    pub fn size(&self, render: &Render) -> (f32, f32) {
        let (x, y) = self.bounds;

        let p1 = (x.0, y.0);
        let p2 = (x.1, y.1);

        let (x1, y1) = render.map(p1).unwrap();
        let (x2, y2) = render.map(p2).unwrap();

        ((x2 - x1).abs(), (y2 - y1).abs())
    }

    pub fn origin(&self, render: &Render) -> (f32, f32) {
        let (x, y) = self.bounds;
        let p1 = (x.0, y.1);
        render.map(p1).unwrap()
    }
}
