#![windows_subsystem = "windows"]

use std::{path::PathBuf, sync::LazyLock};

use egui::ScrollArea;

mod editors;
mod explorer;
mod llm_integration;

mod util;

use crate::{
    editors::{
        asset_editor::Asset, content_editor, note_editor, object_editor::ObjectInstance,
        settings_editor::ProjectSettings, tags::Tag, template_editor::Template,
    },
    explorer::Explorer,
};

static VERSION: &str = "0.1.0";
static PROJECT_FOLDER: LazyLock<PathBuf> = LazyLock::new(|| {
    let mut path = std::env::current_dir().unwrap();
    path.push("project");
    path
});

fn main() {
    let app = Interface::new();
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
        ..Default::default()
    };

    if let Err(e) = eframe::run_native("World Coder", options, Box::new(|_cc| Ok(Box::new(app)))) {
        eprintln!("Failed to run app: {e}");
    }

    eprintln!("App closed");
}

pub struct Interface {
    right_panel_content: RightPanelContent,
    editor: content_editor::MainEditor,
    explorer: Explorer,
    project: ProjectSettings,
}

impl eframe::App for Interface {
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        egui_extras::install_image_loaders(ctx);

        self.configure_appearance(ctx);
        self.render_top_panel(ctx);
        self.render_left_panel(ctx);
        self.render_right_panel(ctx);
        self.render_main_content(ctx);
    }
}

pub enum RightPanelContent {
    Template(Box<Template>),
    Object(Box<ObjectInstance>),
    Note(Box<note_editor::Note>),
    Tag(Tag),
    Asset(Box<Asset>),
    None,
}

impl RightPanelContent {
    fn template(template: Option<Template>) -> Self {
        Self::Template(Box::new(template.unwrap_or_default()))
    }

    fn object(instance: Option<ObjectInstance>) -> Self {
        Self::Object(Box::new(instance.unwrap_or_default()))
    }

    fn note(note: Option<note_editor::Note>) -> Self {
        Self::Note(Box::new(note.unwrap_or_default()))
    }
}

impl Interface {
    #[must_use]
    pub fn new() -> Self {
        Self {
            right_panel_content: RightPanelContent::None,
            editor: content_editor::MainEditor::new(),
            explorer: Explorer::new(),
            project: ProjectSettings::load(),
        }
    }

    fn render_top_panel(&mut self, ctx: &egui::Context) {
        // Top bar with actions
        egui::TopBottomPanel::top("top").show(ctx, |ui| {
            ui.horizontal(|ui| {
                // title widget
                ui.heading("WorldCoder");
                ui.separator();

                // version
                ui.label(VERSION);

                // Settings
                if ui.button("Settings").clicked() {
                    self.project.open();
                }
            });
        });

        if self.project.open {
            let mut open = self.project.open;
            egui::Window::new("Settings")
                .open(&mut open)
                .show(ctx, |ui| {
                    self.project.ui(ui);
                });
            self.project.open = open;
        }
    }

    fn render_left_panel(&mut self, ctx: &egui::Context) {
        // Left panel - File browser
        egui::SidePanel::left("file_browser")
            .resizable(true)
            .default_width(250.0)
            .show(ctx, |ui| {
                ui.heading("Explorer");

                let mut to_load: Option<RightPanelContent> = None;
                let mut load_doc: Option<content_editor::MainEditor> = None;

                ScrollArea::vertical().show(ui, |ui| {
                    self.explorer.ui(&mut to_load, &mut load_doc, ui);
                });

                if let Some(to_load) = to_load {
                    self.right_panel_content = to_load;
                }

                if let Some(load_doc) = load_doc {
                    self.editor = load_doc;
                }
            });
    }

    fn render_right_panel(&mut self, ctx: &egui::Context) {
        // Main content area
        egui::SidePanel::right("templates").show(ctx, |ui| {
            let mut new_instance: Option<RightPanelContent> = None;

            match &mut self.right_panel_content {
                // an instance of a template
                RightPanelContent::Object(object) => {
                    // load template from path

                    let mut right_panel = None;

                    let template = Template::load(&object.template_id).unwrap();
                    ScrollArea::vertical().show(ui, |ui| {
                        object.ui(
                            ui,
                            &template,
                            &mut right_panel,
                            &mut self.explorer.objects(),
                        );
                    });

                    if let Some(right_panel) = right_panel {
                        self.right_panel_content = right_panel;
                    }
                }

                // an editable template
                RightPanelContent::Template(template) => {
                    template.ui(ui, &mut new_instance);
                }

                RightPanelContent::Note(note) => note.ui(ui),

                RightPanelContent::Tag(tag) => tag.ui(ui),

                RightPanelContent::Asset(asset) => asset.ui(ui),

                RightPanelContent::None => {
                    ui.centered_and_justified(|ui| {
                        ui.label("No template loaded to edit.");
                        if ui.button("Back").clicked() {
                            self.right_panel_content = RightPanelContent::None;
                        }
                    });
                }
            };

            if let Some(new) = new_instance {
                self.right_panel_content = new;
            }
        });
    }

    // render main content area
    fn render_main_content(&mut self, ctx: &egui::Context) {
        self.editor.ui(ctx, &mut self.project);
    }

    // configure appearance of UI elements
    fn configure_appearance(&self, ctx: &egui::Context) {
        // configure appearance of UI elements
        let mut visuals = egui::Visuals::dark();
        visuals.window_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.panel_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.widgets.inactive.fg_stroke =
            egui::Stroke::from((1.0, egui::Color32::from_rgb(255, 255, 255)));
        visuals.widgets.inactive.bg_stroke =
            egui::Stroke::from((1.0, egui::Color32::from_rgb(60, 60, 60)));
        visuals.widgets.inactive.corner_radius = egui::CornerRadius::from(4);
        visuals.widgets.inactive.bg_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.widgets.inactive.weak_bg_fill = egui::Color32::from_rgb(20, 20, 20);
        visuals.widgets.inactive.expansion = 1.0;

        ctx.set_visuals(visuals);

        // setup fonts.
        let mut fonts = egui::FontDefinitions::default();
        fonts.font_data.insert(
            "JetBrains Mono Nerd Font".to_string(),
            std::sync::Arc::new(egui::FontData::from_static(include_bytes!(
                "../font/JetBrainsMonoNerdFontMono_Regular.ttf",
            ))),
        );

        fonts
            .families
            .entry(egui::FontFamily::Proportional)
            .or_default()
            .insert(0, "JetBrains Mono Nerd Font".to_string());

        fonts
            .families
            .entry(egui::FontFamily::Monospace)
            .or_default()
            .insert(0, "JetBrains Mono Nerd Font".to_string());

        ctx.set_fonts(fonts);
    }
}

impl Default for Interface {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for Interface {
    fn drop(&mut self) {
        self.project.save();
    }
}
