use enum2str::EnumStr;
use nightshade::prelude::*;
use strum::EnumIter;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, EnumIter, EnumStr)]
pub enum WidgetKind {
    #[default]
    Camera,
}

impl WidgetKind {
    pub fn title(&self) -> String {
        self.to_string()
    }
}

pub struct WidgetContext<'a> {
    pub world: *mut World,
    pub selected_viewport_tile: &'a mut Option<egui_tiles::TileId>,
    pub viewport_textures: &'a Vec<egui::TextureId>,
    pub current_tile_id: egui_tiles::TileId,
}

pub trait Widget {
    fn title(&self) -> String;
    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext);
}

#[derive(Clone, Default)]
pub struct CameraWidget {
    pub camera_index: usize,
}

impl Widget for CameraWidget {
    fn title(&self) -> String {
        format!("Camera {}", self.camera_index + 1)
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        let world = context.world;
        let rect = ui.available_rect_before_wrap();
        let tile_id = context.current_tile_id;

        let mut cameras: Vec<Entity> = (unsafe { &*world }).query_entities(CAMERA).collect();
        cameras.sort_by_key(|entity| entity.id);
        let camera_entity = cameras.get(self.camera_index).copied();

        let texture_index = camera_entity.and_then(|cam| {
            (unsafe { &*world })
                .resources
                .user_interface
                .required_cameras
                .iter()
                .position(|&entity| entity == cam)
        });

        let clicked = if let Some(index) = texture_index
            && let Some(texture_id) = context.viewport_textures.get(index)
        {
            let image = egui::Image::new(egui::load::SizedTexture::new(
                *texture_id,
                egui::vec2(rect.width(), rect.height()),
            ))
            .fit_to_exact_size(egui::vec2(rect.width(), rect.height()))
            .sense(egui::Sense::click());

            let response = ui.put(rect, image);
            response.clicked()
        } else {
            let response = ui.allocate_rect(rect, egui::Sense::click());
            response.clicked()
        };

        let pixels_per_point = ui.ctx().pixels_per_point();

        if context.selected_viewport_tile.is_none() {
            *context.selected_viewport_tile = Some(tile_id);
            if let Some(cam) = camera_entity {
                (unsafe { &mut *world }).resources.active_camera = Some(cam);
            }
        }

        if clicked {
            *context.selected_viewport_tile = Some(tile_id);
            if let Some(cam) = camera_entity {
                (unsafe { &mut *world }).resources.active_camera = Some(cam);
            }
        }

        let is_selected = *context.selected_viewport_tile == Some(tile_id);
        if is_selected {
            (unsafe { &mut *world })
                .resources
                .window
                .active_viewport_rect = Some(nightshade::prelude::ViewportRect {
                x: rect.min.x * pixels_per_point,
                y: rect.min.y * pixels_per_point,
                width: rect.width() * pixels_per_point,
                height: rect.height() * pixels_per_point,
            });
            if let Some(cam) = camera_entity {
                (unsafe { &mut *world }).resources.active_camera = Some(cam);
            }

            ui.painter().rect_stroke(
                rect,
                egui::CornerRadius::ZERO,
                egui::Stroke::new(3.0, egui::Color32::from_rgb(255, 165, 0)),
                egui::StrokeKind::Inside,
            );
        }
    }
}

#[derive(Clone)]
pub enum EditorWidget {
    Camera(CameraWidget),
}

impl EditorWidget {
    pub fn from_kind(kind: WidgetKind) -> Self {
        match kind {
            WidgetKind::Camera => EditorWidget::Camera(CameraWidget::default()),
        }
    }

    pub fn required_camera(&self, world: &World) -> Option<Entity> {
        match self {
            EditorWidget::Camera(widget) => {
                let mut cameras: Vec<Entity> = world.query_entities(CAMERA).collect();
                cameras.sort_by_key(|entity| entity.id);
                cameras.get(widget.camera_index).copied()
            }
        }
    }
}

impl Widget for EditorWidget {
    fn title(&self) -> String {
        match self {
            EditorWidget::Camera(widget) => widget.title(),
        }
    }

    fn ui(&mut self, ui: &mut egui::Ui, context: &mut WidgetContext) {
        match self {
            EditorWidget::Camera(widget) => widget.ui(ui, context),
        }
    }
}

#[derive(Clone)]
pub struct Pane {
    pub widget: EditorWidget,
}

impl Pane {
    pub fn new(kind: WidgetKind) -> Self {
        Self {
            widget: EditorWidget::from_kind(kind),
        }
    }

    pub fn camera(camera_index: usize) -> Self {
        Self {
            widget: EditorWidget::Camera(CameraWidget { camera_index }),
        }
    }
}
