//! Provides traits and tools for displaying windows.

use crate::graphic::icon::Icon;
use crate::io::storage::Storage;
use crate::natives_and_messaging::WindowStyle;
use crate::widget::menubar::MenuBar;

pub mod dialog;

#[cfg(windows)]
#[derive(Clone)]
pub struct Frame {
    /// Window body
    handle: windows_sys::Win32::Foundation::HWND
}

#[cfg(windows)]
impl Frame {
    /// Create a new Frame
    pub fn new(window: windows_sys::Win32::Foundation::HWND) -> Self {
        info!("A window has been created. handle: {}", window);
        Self { handle: window }
    }

    /// Display the window
    pub fn show(&self) {
        use windows_sys::Win32::UI::WindowsAndMessaging::ShowWindow;
        use windows_sys::Win32::UI::WindowsAndMessaging::SW_SHOWNORMAL;
        unsafe {
            ShowWindow(self.handle, SW_SHOWNORMAL);
            info!("A window has appeared. handle: {}", self.handle);
        }
    }

    /// Maximize the window
    pub fn show_maximized(&self) {
        use windows_sys::Win32::UI::WindowsAndMessaging::ShowWindow;
        use windows_sys::Win32::UI::WindowsAndMessaging::SW_MAXIMIZE;
        unsafe {
            ShowWindow(self.handle, SW_MAXIMIZE);
            info!("The window has been maximized. handle: {}", self.handle);
        }
    }

    /// Minimize the window
    pub fn show_minimized(&self) {
        use windows_sys::Win32::UI::WindowsAndMessaging::ShowWindow;
        use windows_sys::Win32::UI::WindowsAndMessaging::SW_MINIMIZE;
        unsafe {
            ShowWindow(self.handle, SW_MINIMIZE);
            info!("The window has been minimized. handle: {}", self.handle);
        }
    }

    /// Hide the window
    pub fn hide(&self) {
        use windows_sys::Win32::UI::WindowsAndMessaging::ShowWindow;
        use windows_sys::Win32::UI::WindowsAndMessaging::SW_HIDE;
        unsafe {
            ShowWindow(self.handle, SW_HIDE);
            info!("The window has been hidden. handle: {}", self.handle);
        }
    }

    /// Returns the window handle.
    pub fn get_window(&self) -> windows_sys::Win32::Foundation::HWND {
        self.handle
    }

    /// Set window title
    pub fn set_title(&self, title: String) {
        use windows_sys::Win32::UI::WindowsAndMessaging::SetWindowTextA;
        unsafe {
            SetWindowTextA(self.handle, title.as_ptr() as _);
            info!(
                "The title of the window has been changed to '{}'. handle: {}",
                title, self.handle
            );
        }
    }

    /// Set window position
    pub fn set_position(&self, x: i32, y: i32) {
        use windows_sys::Win32::UI::WindowsAndMessaging::SetWindowPos;
        unsafe {
            SetWindowPos(self.handle, 0, x, y, x, y, 0);
        }
    }

    /// Set window size
    /// **Warning: This method is unstable.**
    pub fn set_size(&self, width: i32, height: i32) {
        use windows_sys::Win32::Foundation::RECT;
        use windows_sys::Win32::UI::WindowsAndMessaging::{SetWindowPos,GetWindowRect,ShowWindow,SW_SHOWDEFAULT};
        unsafe {
            let rect = &mut RECT {
                left: 0,
                top: 0,
                right: 0,
                bottom: 0,
            };
            GetWindowRect(self.handle, rect);

            ShowWindow(self.handle, SW_SHOWDEFAULT);
            SetWindowPos(self.handle, 0, rect.left, rect.top, width, height, 0);
        }
    }

    pub fn set_menu(self, menu: MenuBar) {
        use windows_sys::Win32::UI::WindowsAndMessaging::SetMenu;
        unsafe {
            SetMenu(self.handle, menu.get_menu());
        }
    }

    pub fn set_icon(&self, icon: Icon) {
        use windows_sys::Win32::Foundation::WPARAM;
        use windows_sys::Win32::UI::WindowsAndMessaging::*;
        unsafe {
            SendMessageA(self.handle, WM_SETICON, ICON_BIG as WPARAM, icon.get_icon());
            SendMessageA(
                self.handle,
                WM_SETICON,
                ICON_SMALL as WPARAM,
                icon.get_icon(),
            );
        }
    }

    pub fn set_blur(&self) {
        use windows_sys::Win32::Graphics::Dwm::*;
        use windows_sys::Win32::Foundation::BOOL;
        unsafe {
            let bb = &mut DWM_BLURBEHIND {
                dwFlags: DWM_BB_ENABLE,
                fEnable: BOOL::from(true),
                hRgnBlur: 0,
                fTransitionOnMaximized: 0
            };
            DwmEnableBlurBehindWindow(self.handle,bb);
        }
    }
}

#[cfg(windows)]
impl Default for Frame {
    fn default() -> Self {
        Self { handle: 0 }
    }
}

#[cfg(windows)]
pub struct Attributes {
    pub style: windows_sys::Win32::UI::WindowsAndMessaging::WINDOW_STYLE,
    pub title: String,
    pub app_id: String,
    pub width: i32,
    pub height: i32,
    pub x: i32,
    pub y: i32,
}

#[cfg(windows)]
impl Attributes {
    pub fn new(
        style: WindowStyle,
        title: String,
        app_id: String,
        width: i32,
        height: i32,
        x: i32,
        y: i32,
    ) -> Self {
        Self {
            style,
            title,
            app_id,
            width,
            height,
            x,
            y,
        }
    }
}

#[cfg(windows)]
pub struct AdvancedOptions {
    advanced_style: windows_sys::Win32::UI::WindowsAndMessaging::WINDOW_EX_STYLE,
    background_color: i32
}

#[cfg(windows)]
impl AdvancedOptions {
    pub fn new(
        advanced_style: windows_sys::Win32::UI::WindowsAndMessaging::WINDOW_EX_STYLE,background_color: i32
    ) -> Self {
        Self { advanced_style,background_color }
    }

    pub fn get_style(&self) -> windows_sys::Win32::UI::WindowsAndMessaging::WINDOW_EX_STYLE {
        self.advanced_style
    }

    pub fn get_background_color(&self) -> i32 {
        self.background_color
    }
}

#[cfg(windows)]
impl Default for AdvancedOptions {
    fn default() -> Self {
        Self { advanced_style: 0, background_color: 5 }
    }
}

#[cfg(windows)]
pub trait Application {
    /// Returns the name of the application.
    fn app_name(&self) -> String;
    /// Returns WindowAttributes.
    fn attributes(&self) -> Attributes;

    fn is_child_window(&self) -> bool;

    fn advanced_options(&self) -> AdvancedOptions {
        AdvancedOptions::default()
    }

    /// Processing at application startup (optional)
    fn startup(&self, _storage: Storage) {}

    /// Writing UI processes and events
    fn ui(&self, frame: &Frame);
    /// Processing at application termination (optional)
    fn exit(&self) {}
}

#[cfg(unix)]
#[derive(Clone)]
pub struct Frame {
    /// Window body
    handle: gtk::ApplicationWindow,
    application: gtk::Application,
}

#[cfg(unix)]
impl Frame {
    /// Create a new Frame
    pub fn new(window: gtk::ApplicationWindow, application: Option<gtk::Application>) -> Self {
        info!("A window has been created. handle: {}", window);
        let app = match application {
            Some(a) => a,
            None => panic!("Application is None!"),
        };
        Self {
            handle: window,
            application: app,
        }
    }

    /// Display the window
    pub fn show(&self) {
        use gtk::prelude::*;
        self.handle.show_all();
        info!("A window has appeared. handle: {}", self.handle);
    }

    /// Maximize the window
    pub fn show_maximized(&self) {
        use gtk::prelude::*;
        self.handle.maximize();
        info!("The window has been maximized. handle: {}", self.handle);
    }

    /// Minimize the window
    pub fn show_minimized(&self) {
        use gtk::prelude::*;
        self.handle.unmaximize();
        info!("The window has been minimized handle: {}", self.handle);
    }

    /// Hide the window
    pub fn hide(&self) {
        use gtk::prelude::*;
        self.handle.hide();
        info!("The window has been hidden. handle: {}", self.handle)
    }

    /// Set window title
    pub fn set_title(&self, title: String) {
        use gtk::prelude::*;
        self.handle.set_title(title.as_str());
        info!(
            "The title of the window has been changed to '{}'. handle: {}",
            title, self.handle
        );
    }

    /// Returns the window handle.
    pub fn get_window(&self) -> gtk::ApplicationWindow {
        use std::borrow::Borrow;
        self.handle.borrow().clone()
    }

    /// Set window position
    pub fn set_position(&self, x: i32, y: i32) {
        use gtk::prelude::*;
        self.handle.set_window_position(gtk::WindowPosition::Center);
    }

    /// Set window size
    pub fn set_size(&self, width: i32, height: i32) {
        use gtk::prelude::*;
        self.handle.set_default_size(width, height);
    }

    pub fn set_menu(&self, menu: MenuBar) {
        use gtk::prelude::*;
        self.application.set_menubar(Some(&menu.get_menu()));
    }

    pub fn set_icon(&self, icon: Icon) {
        use gtk::prelude::*;
        use std::path::Path;
        self.handle
            .set_icon_from_file(&Path::new(icon.get_file_name().as_str()));
    }
}

#[cfg(unix)]
impl Default for Frame {
    fn default() -> Self {
        Self {
            handle: gtk::ApplicationWindow::new(&gtk::Application::new(
                Some("com.example.demo"),
                Default::default(),
            )),
            application: gtk::Application::new(
                Option::from("com.example.demo"),
                Default::default(),
            ),
        }
    }
}

#[cfg(unix)]
#[derive(Clone)]
pub struct Attributes {
    pub style: gtk::WindowType,
    pub title: String,
    pub app_id: String,
    pub width: i32,
    pub height: i32,
    pub x: i32,
    pub y: i32,
}

#[cfg(unix)]
impl Attributes {
    pub fn new(
        style: gtk::WindowType,
        title: String,
        app_id: String,
        width: i32,
        height: i32,
        x: i32,
        y: i32,
    ) -> Self {
        Self {
            style,
            title,
            app_id,
            width,
            height,
            x,
            y,
        }
    }
}

#[cfg(unix)]
#[derive(Clone)]
pub struct AdvancedOptions {
    advanced_style: gtk::gdk::WindowTypeHint,
}

#[cfg(unix)]
impl AdvancedOptions {
    pub fn new(advanced_style: gtk::gdk::WindowTypeHint) -> Self {
        Self { advanced_style }
    }

    pub fn get_style(&self) -> gtk::gdk::WindowTypeHint {
        self.advanced_style
    }
}

#[cfg(unix)]
impl Default for AdvancedOptions {
    fn default() -> Self {
        Self {
            advanced_style: gtk::gdk::WindowTypeHint::Normal,
        }
    }
}

#[cfg(unix)]
pub trait Application {
    /// Returns the name of the window.
    fn app_name(&self) -> String;
    /// Returns WindowAttributes.
    fn attributes(&self) -> Attributes;

    fn is_child_window(&self) -> bool;

    fn advanced_options(&self) -> AdvancedOptions {
        AdvancedOptions::default()
    }

    /// Processing at application startup (optional)
    fn startup(&self, storage: Storage) {}
    /// Writing UI processes and events
    fn ui(&self, frame: &Frame);
    /// Processing at application termination (optional)
    fn exit(&self) {}
}
