use godot::prelude::*;
use godot::classes::{ConfigFile, Engine, DisplayServer, display_server::{WindowFlags, WindowMode, VSyncMode}, window::WindowInitialPosition};
#[derive(GodotClass)]
#[class(base=Node)]

struct Config {
    // DISCUSSION: Should we expose these in-editor so the default values can be modified?

    #[export(file = "")]
    config_path: GString,
    #[var]
    window_height: i32,
    #[var]
    window_width: i32,
    #[var]
    is_fullscreen: bool,
    #[var]
    is_borderless: bool,
    #[var]
    monitor: i32,
    #[var]
    window_position_x: i32,
    #[var]
    window_position_y: i32,
    #[var]
    max_framerate: i32,
    #[var]
    vertical_sync: bool,
    #[var]
    resolution_scale: f32,


    base: Base<Node>,
}

#[godot_api]
impl INode for Config {
    fn init(base: Base<Node>) -> Self {

        Self {
            config_path: GString::from("res://config.toml"),
            window_height: 600,
            window_width: 400,
            is_fullscreen: false,
            is_borderless: true,
            monitor: 0,
            window_position_x: 0,
            window_position_y: 0,
            max_framerate: 60,
            vertical_sync: true,
            resolution_scale: 1.0,

            base,

        }
    }
    fn enter_tree(&mut self) {
        let editor_hint = Engine::singleton().is_editor_hint();

        let mut config = ConfigFile::new_gd();

        let res = config.load(GString::from("res://config.toml"));

        if res != godot::global::Error::OK {
            godot_error!("Failed to load config file");
        }

        else {
            //TODO This could be improved
            //// GDext doesn's support defining defaults at this moment, and there's no way in hell I'm implementing checks for each config key.
            //// If a config field is missing, it should throw an error, but not halt the whole demo
            //
            // We can get the keys and sections with get_sections() and get_section_keys() and then iterate over each one and load them if they match with the Config struct
            //
            self.window_width = config.get_value(GString::from("window"), GString::from("width")).to::<i32>();
            self.window_height = config.get_value(GString::from("window"), GString::from("height")).to::<i32>();
            self.is_fullscreen = config.get_value(GString::from("window"), GString::from("fullscreen")).to::<bool>();
            self.is_borderless = config.get_value(GString::from("window"), GString::from("borderless")).to::<bool>();
            self.window_position_x = config.get_value(GString::from("window"), GString::from("position_x")).to::<i32>();
            self.window_position_y = config.get_value(GString::from("window"), GString::from("position_y")).to::<i32>();
            self.monitor = config.get_value(GString::from("window"), GString::from("monitor")).to::<i32>();

            self.max_framerate = config.get_value(GString::from("graphics"), GString::from("max_framerate")).to::<i32>();
            self.vertical_sync = config.get_value(GString::from("graphics"), GString::from("vertical_sync")).to::<bool>();
            self.resolution_scale = config.get_value(GString::from("graphics"), GString::from("resolution_scale")).to::<f32>();
        }
        if !editor_hint {
            // THIS CHECK IS VERY IMPORTANT!!! I don't want to screw my godot up again by resizing the editor and moving it out of bounds..
            Self::apply_video_settings(self);
        }
    }
}

#[godot_api]
impl Config {
    #[func]
    fn apply_video_settings(&mut self) {
        let mut disp_server = DisplayServer::singleton();
        let mut window = self.base().get_window().unwrap();
        let mut engine = Engine::singleton();
        let initial_window_size = disp_server.window_get_size();

        let window_offset_after_resize: Vector2i = Vector2i::from_array([(self.window_width - initial_window_size.x) / 2, (self.window_height - initial_window_size.y) / 2]);
        if self.is_fullscreen {
            disp_server.window_set_mode(WindowMode::FULLSCREEN)
        }
        else {
            disp_server.window_set_mode(WindowMode::WINDOWED)
        }
        
        if self.vertical_sync {
            disp_server.window_set_vsync_mode(VSyncMode::ENABLED);
        }
        else {
            disp_server.window_set_vsync_mode(VSyncMode::DISABLED);
        }
        
        disp_server.window_set_flag(WindowFlags::BORDERLESS, self.is_borderless);
        disp_server.window_set_current_screen(self.monitor);
        disp_server.window_set_size(Vector2i::from_array([self.window_width, self.window_height]));
        
        //FIXME: Doesn't seem to work on Linux, at least not on KDE+Wayland. Most likely an issue with Godot itself
        //       as trying to set the window position from GDscript doesn't work either...
        //       EDIT: It worked for a moment, then broke again... weird
        window.set_initial_position(WindowInitialPosition::ABSOLUTE);
        
        if cfg!(debug_assertions) {
            godot_print!("WINDOW SIZE: {}, {}", initial_window_size.x, initial_window_size.y);
            godot_print!("Initial size: {}, {}", initial_window_size.x, initial_window_size.y);
            godot_print!("offset: {}, {}", window_offset_after_resize.x, window_offset_after_resize.y);
            godot_print!("New pos: {}, {}", self.window_position_x - window_offset_after_resize.x, self.window_position_y - window_offset_after_resize.y);
        }

        window.set_position(Vector2i::from_array([self.window_position_x - window_offset_after_resize.x, initial_window_size.y - window_offset_after_resize.y]));
        engine.set_max_fps(self.max_framerate);


    }
}