use fltk::{
    app::*,
    dialog::*,
    menu::*,
    text::{TextBuffer, TextEditor},
    window::Window,
};
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::{fs, path};

pub struct Editor {
    pub editor: TextEditor,
    filename: String,
}

impl Editor {
    pub fn new(mut buf: &mut TextBuffer) -> Editor {
        Editor {
            editor: TextEditor::new(5, 40, 790, 555, &mut buf),
            filename: String::from(""),
        }
    }

    pub fn filename(&self) -> String {
        self.filename.clone()
    }

    pub fn set_filename(&mut self, name: &str) {
        self.filename = String::from(name);
    }

    pub fn style(&mut self) {
        self.editor.set_text_font(Font::Courrier);
        self.editor.set_linenumber_width(18);
        self.editor
            .set_linenumber_fgcolor(Color::from_u32(0x8b8386));
        self.editor.set_trigger(CallbackTrigger::Changed);
    }

    pub fn save_file(&mut self, saved: &mut bool) {
        let mut filename = self.filename.clone();
        if filename.is_empty() {
            let mut dlg = FileDialog::new(FileDialogType::BrowseSaveFile);
            dlg.set_option(FileDialogOptions::SaveAsConfirm);
            dlg.show();
            filename = dlg.filename().to_string_lossy().to_string();
            if filename.is_empty() {
                return;
            }
            match path::Path::new(&filename).exists() {
                true => {
                    fs::write(&filename, self.editor.buffer().text()).unwrap();
                    *saved = true;
                }
                false => alert("Please specify a file!"),
            }
        } else {
            match path::Path::new(&filename).exists() {
                true => {
                    fs::write(&filename, self.editor.buffer().text()).unwrap();
                    *saved = true;
                }
                false => alert("Please specify a file!"),
            }
        }
    }
}

impl Deref for Editor {
    type Target = TextEditor;

    fn deref(&self) -> &Self::Target {
        &self.editor
    }
}

impl DerefMut for Editor {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.editor
    }
}

fn main() {
    let app = App::default().set_scheme(AppScheme::Gtk);

    let mut wind = Window::default()
        .with_size(800, 600)
        .center_screen()
        .with_label("RustyEd");

    let mut menu = MenuBar::new(0, 0, 800, 40, "");
    menu.set_color(Color::Light2);

    let buf = TextBuffer::default();
    let buf = Rc::from(RefCell::from(buf));
    buf.borrow_mut().set_tab_distance(4);

    let mut editor = Editor::new(buf.borrow_mut().deref_mut());

    editor.style();
    wind.make_resizable(true);
    wind.end();
    wind.show();

    let mut saved = false;

    let editor_rc = Rc::from(RefCell::from(editor));

    let editor_cloned = editor_rc.clone();
    editor_cloned
        .borrow_mut()
        .editor
        .set_callback(Box::new(move || saved = false));

    let editor_cloned = editor_rc.clone();
    menu.add(
        "File/New...",
        Shortcut::Ctrl + 'n',
        MenuFlag::Normal,
        Box::new(move || {
            if editor_cloned.borrow().buffer().text() != "" {
                let x = choice("File unsaved, Do you wish to continue?", "Yes", "No!", "");
                if x == 0 {
                    editor_cloned.borrow().buffer().set_text("");
                }
            }
        }),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "File/Open...",
        Shortcut::Ctrl + 'o',
        MenuFlag::Normal,
        Box::new(move || {
            let mut dlg = FileDialog::new(FileDialogType::BrowseFile);
            dlg.set_option(FileDialogOptions::NoOptions);
            dlg.set_filter("*.txt");
            dlg.show();
            editor_cloned
                .borrow_mut()
                .set_filename(&dlg.filename().to_string_lossy().to_string());
            if editor_cloned.borrow().filename.is_empty() {
                return;
            }
            match path::Path::new(&editor_cloned.borrow().filename()).exists() {
                true => editor_cloned.borrow().buffer().set_text(
                    fs::read_to_string(&editor_cloned.borrow().filename())
                        .unwrap()
                        .as_str(),
                ),
                false => alert("File does not exist!"),
            }
        }),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "File/Save",
        Shortcut::Ctrl + 's',
        MenuFlag::Normal,
        Box::new(move || editor_cloned.borrow_mut().save_file(&mut saved)),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "File/Save as...",
        Shortcut::None,
        MenuFlag::MenuDivider,
        Box::new(move || editor_cloned.borrow_mut().save_file(&mut saved)),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "File/Quit",
        Shortcut::None,
        MenuFlag::Normal,
        Box::new(move || {
            if saved == false {
                let x = choice("Would you like to save your work?", "Yes", "No", "");
                if x == 0 {
                    editor_cloned.borrow_mut().save_file(&mut saved);
                    std::process::exit(0);
                } else {
                    std::process::exit(0);
                }
            } else {
                std::process::exit(0);
            }
        }),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "Edit/Cut",
        Shortcut::Ctrl + 'x',
        MenuFlag::Normal,
        Box::new(move || editor_cloned.borrow().cut()),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "Edit/Copy",
        Shortcut::Ctrl + 'c',
        MenuFlag::Normal,
        Box::new(move || {
            editor_cloned.borrow().copy();
        }),
    );

    let editor_cloned = editor_rc.clone();
    menu.add(
        "Edit/Paste",
        Shortcut::Ctrl + 'v',
        MenuFlag::Normal,
        Box::new(move || editor_cloned.borrow().paste()),
    );

    menu.add(
        "Help/About",
        Shortcut::None,
        MenuFlag::Normal,
        Box::new(move || {
            message(
                "This is an example application written in Rust and using the FLTK Gui library.",
            );
        }),
    );

    let mut x = menu.item("File/Quit").unwrap();
    x.set_label_color(Color::Red);

    let editor_cloned = editor_rc.clone();
    wind.set_callback(Box::new(move || {
        if fltk::app::event() == fltk::app::Event::Close {
            if saved == false {
                let x = choice("Would you like to save your work?", "Yes", "No", "");
                if x == 0 {
                    editor_cloned.borrow_mut().save_file(&mut saved);
                    std::process::exit(0);
                } else {
                    std::process::exit(0);
                }
            } else {
                std::process::exit(0);
            }
        }
    }));
    
    app.run().expect("Couldn't run editor");
}
