use std::any::Any;
use std::iter::Enumerate;

use egui::{widgets, TextStyle};
use crate::metadata::{AgeRating, LanguageISO, Metadata};

use eframe::egui;
use strum::IntoEnumIterator;
use crate::parsers::{MetadataSource, Parser};

mod metadata;
mod parsers;

const TEXT_SIZE_SMALL: f32 = 1.0;
const TEXT_SIZE_MEDIUM: f32 = 1.2;
const TEXT_SIZE_LARGE: f32 = 1.4;

fn main() -> Result<(), eframe::Error> {
    env_logger::init();
    let options = eframe::NativeOptions {
        initial_window_size: Some(egui::vec2(1280.0, 720.0)),
        ..Default::default()
    };
    eframe::run_native(
        "Metadata Generator",
        options,
        Box::new(|cc| {
            cc.egui_ctx.set_pixels_per_point(TEXT_SIZE_MEDIUM);
            Box::<Generator>::default()
        }),
    )
}

struct Generator<'a> {
    show_all: bool,
    metadata: Vec<Metadata<'a>>,
    selected_parser: Box<dyn Parser>,
    meta_path: Option<String>,
    bundle: bool,
    bundle_source: Option<String>,
    bundle_path: Option<String>,
    footer_height: f32,
}

impl Default for Generator<'_> {
    fn default() -> Self {
        Self {
            show_all: false,
            metadata: vec![Metadata::default()],
            selected_parser: Box::new(parsers::file::File::new()),
            meta_path: None,
            bundle: false,
            bundle_source: None,
            bundle_path: None,
            footer_height: 0.0,
        }
    }
}

impl eframe::App for Generator<'_> {
    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
        let ParserList:  Vec<Box<dyn Parser>> = vec![
            Box::new(parsers::fakku::Fakku::new()),
            Box::new(parsers::file::File::new()),
            Box::new(parsers::irodori::Irodori::new()),
            Box::new(parsers::jnovel::JNovel::new()),
        ];

        // Non-Metadata Options
        egui::SidePanel::right("Options")
            .resizable(false)
            .min_width(ctx.screen_rect().width() / 3.0)
            .max_width(ctx.screen_rect().width() / 3.0)
            .show(ctx, |panel| {
                panel.heading("Options");
                panel.horizontal(|row| {
                    row.label("Parser: ");
                    egui::containers::ComboBox::from_label("")
                        .selected_text(self.selected_parser.get_display_string())
                        .show_ui(row, |ui| {
                            for parser in ParserList {
                                let text = parser.get_display_string().clone();
                                if ui.selectable_value(&mut self.selected_parser.get_display_string(), parser.get_display_string(), text).clicked() {
                                    self.selected_parser = parser;
                                };
                            }
                        });
                });

                let file_label;
                if self.selected_parser.source_is_file() {
                    file_label = "File Path";
                } else {
                    file_label = "URL";
                }
                text_input_option(panel, &mut self.meta_path, file_label, self.selected_parser.source_is_file());

                // TODO this feels wrong, there must be a better way
                panel.add_space(self.footer_height * 0.1);
                panel.separator();
                panel.add_space(self.footer_height * 0.1);

                panel.horizontal(|row| {
                    row.label("Perform Bundle: ");
                    row.add(widgets::Checkbox::new(&mut self.bundle, ""));
                });

                if self.bundle {
                    text_input_option(panel, &mut self.bundle_source, "Images Folder", true);
                    text_input_option(panel, &mut self.bundle_path, "Output Path", true);
                }

                panel.add_space(self.footer_height * 2.0);

                panel.horizontal(|elem| {
                    elem.vertical_centered(|centered| {
                        if centered.button("Generate Metadata").clicked() {
                            // Run Metadata Generation
                            // DO NOT SAVE RESULTS YET ONLY POPULATE DATA
                            println!("Ran Generation (not yet)");
                        }
                    })
                });

                panel.add_space(panel.available_height() - self.footer_height);

                self.footer_height = panel
                    .horizontal(|row| {
                        let ppp = ctx.pixels_per_point();
                        row.label("Text Size:");
                        if row
                            .add(widgets::RadioButton::new(ppp == TEXT_SIZE_SMALL, "Small"))
                            .clicked()
                        {
                            ctx.set_pixels_per_point(TEXT_SIZE_SMALL);
                        }

                        if row
                            .add(widgets::RadioButton::new(ppp == TEXT_SIZE_MEDIUM, "Medium"))
                            .clicked()
                        {
                            ctx.set_pixels_per_point(TEXT_SIZE_MEDIUM);
                        }

                        if row
                            .add(widgets::RadioButton::new(ppp == TEXT_SIZE_LARGE, "Large"))
                            .clicked()
                        {
                            ctx.set_pixels_per_point(TEXT_SIZE_LARGE);
                        }
                    })
                    .response
                    .rect
                    .height();
            });

        // Metadata Options
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("Metadata");

            // Title & Series
            text_input(ui, &mut self.metadata[0].series, "Series", false);
            text_input(ui, &mut self.metadata[0].title, "Title", false);

            // Volume and Chapter Info
            ui.horizontal(|row| {
                row.label("Volume:");
                if row.button("-").clicked() {
                    self.metadata[0].volume -= 1;
                }
                row.label(format!("{}", self.metadata[0].volume));
                if row.button("+").clicked() {
                    self.metadata[0].volume += 1;
                }

                row.label("Chapter:");
                if row.button("-").clicked() {
                    self.metadata[0].number -= 1;
                }
                row.label(format!("{}", self.metadata[0].number));
                if row.button("+").clicked() {
                    self.metadata[0].number += 1;
                }
            });

            ui.horizontal(|row| {
                row.label("Series Chapter Count:");
                if row.button("-").clicked() {
                    self.metadata[0].count -= 1;
                }
                row.label(format!("{}", self.metadata[0].count));
                if row.button("+").clicked() {
                    self.metadata[0].count += 1;
                }
            });

            ui.label("Summary Edit:");
            let mut summary = self.metadata[0].summary.clone().unwrap_or("".to_string());
            if ui.add(egui::TextEdit::multiline(&mut summary)).changed() {
                self.metadata[0].summary = Some(summary);
            };

            // Date Inputs
            let glyph_width = ctx
                .fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(ui.style()), '0') });

            let mut year_text = self.metadata[0].year.to_string();

            ui.horizontal(|row| {
                row.label("Release Date:");
                if row
                    .add(
                        widgets::text_edit::TextEdit::singleline(&mut year_text)
                            .desired_width(5.0 * glyph_width),
                    )
                    .changed()
                {
                    let trimmed = year_text.trim();
                    if trimmed == "0" {
                        self.metadata[0].year = -1;
                        self.metadata[0].month = -1;
                        self.metadata[0].day = -1;
                    } else {
                        self.metadata[0].year =
                            trimmed.parse::<i16>().unwrap_or(self.metadata[0].year);
                    }
                };

                if self.metadata[0].year >= 1970 {
                    let mut month_text = self.metadata[0].month.to_string();
                    if row
                        .add(
                            widgets::text_edit::TextEdit::singleline(&mut month_text)
                                .desired_width(3.0 * glyph_width),
                        )
                        .changed()
                    {
                        let trimmed = month_text.trim();
                        if trimmed == "0" {
                            self.metadata[0].month = -1;
                            self.metadata[0].day = -1;
                        } else {
                            self.metadata[0].month = month_text
                                .trim()
                                .parse::<i8>()
                                .unwrap_or(self.metadata[0].month);
                        }
                    };

                    if self.metadata[0].month >= 0 {
                        let mut day_text = self.metadata[0].day.to_string();
                        if row
                            .add(
                                widgets::text_edit::TextEdit::singleline(&mut day_text)
                                    .desired_width(3.0 * glyph_width),
                            )
                            .changed()
                        {
                            let trimmed = day_text.trim();
                            if trimmed == "0" {
                                self.metadata[0].day = -1;
                            } else {
                                self.metadata[0].day = day_text
                                    .trim()
                                    .parse::<i8>()
                                    .unwrap_or(self.metadata[0].day);
                            }
                        };
                    }
                }
            });

            // People involved
            text_input_option(ui, &mut self.metadata[0].writer, "Author", false);
            text_input_option(ui, &mut self.metadata[0].letterer, "Typesetter", false);
            text_input_option(ui, &mut self.metadata[0].editor, "Editor", false);
            text_input_option(ui, &mut self.metadata[0].translator, "Translator", false);
            text_input_option(ui, &mut self.metadata[0].publisher, "Publisher", false);

            /*
            tags: Vec<String>,
            characters: Vec<String>,
            */
            // TODO tag list (List of bordered labels with plus that opens a new text edit)

            ui.horizontal(|row| {
                row.label("Tags:");
                for mut tag in &mut self.metadata[0].tags {
                    text_input(row, &mut tag, "", false);
                }
                if row.button("+").clicked() {
                    &self.metadata[0].tags.push("".to_string());
                };
            });

            text_input(ui, &mut self.metadata[0].genre, "Genre", false);
            ui.horizontal(|row| {
                row.label("Page Count:");
                if row.button("-").clicked() {
                    self.metadata[0].page_count -= 1;
                }
                row.label(format!("{}", self.metadata[0].page_count));
                if row.button("+").clicked() {
                    self.metadata[0].page_count += 1;
                }
            });
            //text_input(ui, &mut self.metadata[0].page_count, "Page Count", false);

            // Lang Drop Down
            ui.horizontal(|row| {
                row.label("Language: ");
                egui::containers::ComboBox::from_label("a")
                    .selected_text(self.metadata[0].language.to_string())
                    .show_ui(row, |ui| {
                        for language in LanguageISO::iter() {
                            let text = language.to_string().clone();
                            ui.selectable_value(&mut self.metadata[0].language, language, text);
                        }
                    });
            });

            // TODO same as with Tags but for characters

            // Age Rating Drop Down
            ui.horizontal(|row| {
                row.label("Age Rating: ");
                egui::containers::ComboBox::from_label("b")
                    .selected_text(self.metadata[0].age_rating.to_string())
                    .show_ui(row, |ui| {
                        for rating in AgeRating::iter() {
                            let text = rating.to_string().clone();
                            ui.selectable_value(&mut self.metadata[0].age_rating, rating, text);
                        }
                    });
            });

            ui.horizontal(|elem| {
                elem.vertical_centered(|centered| {
                    if centered.button("Save Metadata").clicked() {
                        // Run Metadata Generation
                        // DO NOT SAVE RESULTS YET ONLY POPULATE DATA
                        self.metadata[0].save_to_xml("/home/neshura/Repositories/comicinfo-editor/ComicInfo.xml")
                    }
                })
            });

            if self.show_all {
                ui.label(format!("Not Implemented yet."));
            }
        });
    }
}

pub(crate) fn text_input_option(
    ui: &mut egui::Ui,
    option: &mut Option<String>,
    label: &str,
    file_picker: bool,
) -> egui::InnerResponse<()> {
    let zero_width =
        2.0 * ui.fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(ui.style()), '0') });

    ui.horizontal(|row| {
        row.label(format!("{}:", label));
        if let Some(ref mut text) = option {
            let dynamic_width = zero_width
                + text
                    .chars()
                    .map(|c| {
                        row.fonts(|f| -> f32 {
                            f.glyph_width(&TextStyle::Body.resolve(row.style()), c)
                        })
                    })
                    .sum::<f32>();

            row.add(
                widgets::text_edit::TextEdit::singleline(text).desired_width(
                    if dynamic_width >= zero_width * 3.0 {
                        dynamic_width
                    } else {
                        zero_width * 3.0
                    },
                ),
            );
            if text == "" {
                *option = None;
            }
        } else {
            let mut init = "".to_string();
            row.add(
                widgets::text_edit::TextEdit::singleline(&mut init).desired_width(zero_width * 3.0),
            );
            if init != "" {
                *option = Some(init);
            }
        }

        if file_picker {
            if row.button("...").clicked() {
                if let Some(path) = rfd::FileDialog::new().pick_file() {
                    *option = Some(path.display().to_string());
                }
            }
        }
    })
}

pub(crate) fn text_input(
    ui: &mut egui::Ui,
    option: &mut String,
    label: &str,
    file_picker: bool,
) -> egui::InnerResponse<()> {
    let zero_width =
        2.0 * ui.fonts(|f| -> f32 { f.glyph_width(&TextStyle::Body.resolve(ui.style()), '0') });

    ui.horizontal(|row| {
        row.label(format!("{}:", label));
        let dynamic_width = zero_width
            + option
                .chars()
                .map(|c| {
                    row.fonts(|f| -> f32 {
                        f.glyph_width(&TextStyle::Body.resolve(row.style()), c)
                    })
                })
                .sum::<f32>();

        row.add(
            widgets::text_edit::TextEdit::singleline(option).desired_width(
                if dynamic_width >= zero_width * 3.0 {
                    dynamic_width
                } else {
                    zero_width * 3.0
                },
            ),
        );

        if file_picker {
            if row.button("...").clicked() {
                if let Some(path) = rfd::FileDialog::new().pick_file() {
                    *option = path.display().to_string();
                }
            }
        }
    })
}