//! Dialog to select a directory.

use std::ptr::null_mut;

#[cfg(windows)]
pub struct DirectoryChooser<'a> {
    pub title: &'a str,
}

#[cfg(windows)]
impl<'a> DirectoryChooser<'a> {
    #[no_mangle] pub extern "C" fn new(title: &'a str) -> Self {
        Self { title }
    }

    #[no_mangle] pub extern "C" fn show(&self) -> String {
        unsafe {
            use crate::util::pwstr_to_string;
            use winapi::shared::winerror::SUCCEEDED;
            use winapi::um::combaseapi::{
                CoCreateInstance, CoInitializeEx, CoUninitialize, CLSCTX_ALL,
            };
            use winapi::um::objbase::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE};
            use winapi::um::shobjidl::*;
            use winapi::um::shobjidl_core::{IShellItem, SIGDN_FILESYSPATH};
            use winapi::um::winnt::PWSTR;
            use winapi::Interface;

            #[allow(non_upper_case_globals)]
            const CLSID_FileOpenDialog: winapi::shared::guiddef::GUID =
                winapi::shared::guiddef::GUID {
                    Data1: 0xdc1c5a9c,
                    Data2: 0xe88a,
                    Data3: 0x4dde,
                    Data4: [0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7],
                };
            let mut sel_dir: String = "".to_string();

            let mut hr = CoInitializeEx(
                null_mut(),
                COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE,
            );
            if SUCCEEDED(hr) {
                let mut pfd: *mut IFileDialog = std::mem::uninitialized();
                hr = CoCreateInstance(
                    &CLSID_FileOpenDialog,
                    null_mut(),
                    CLSCTX_ALL,
                    &IFileOpenDialog::uuidof(),
                    &mut pfd as *mut *mut IFileDialog as *mut _,
                );
                if SUCCEEDED(hr) {
                    let mut fop: FILEOPENDIALOGOPTIONS = std::mem::zeroed();
                    if SUCCEEDED((*pfd).GetOptions(&mut fop)) {
                        (*pfd).SetOptions(
                            fop | FOS_PICKFOLDERS
                                | FOS_FORCESHOWHIDDEN
                                | FOS_PATHMUSTEXIST
                                | FOS_FORCEFILESYSTEM,
                        );
                    }
                    if SUCCEEDED((*pfd).Show(null_mut())) {
                        let mut psi: *mut IShellItem = std::mem::zeroed();
                        if SUCCEEDED((*pfd).GetResult(&mut psi)) {
                            // Provide a pointer to a buffer so windows can swap it for its own buffer
                            let mut buffer: PWSTR = std::ptr::null_mut();
                            if SUCCEEDED((*psi).GetDisplayName(SIGDN_FILESYSPATH, &mut buffer)) {
                                sel_dir = pwstr_to_string(buffer);
                            }
                            winapi::um::combaseapi::CoTaskMemFree(std::mem::transmute(buffer));
                        }
                        (*psi).Release();
                    }
                    (*pfd).Release();
                }
                CoUninitialize();
            }
            sel_dir
        }
    }
}

#[cfg(unix)]
pub struct DirectoryChooser<'a> {
    pub title: &'a str,
}

#[cfg(unix)]
impl<'a> DirectoryChooser<'a> {
    #[no_mangle] pub extern "C" fn new(title: &'a str) -> Self {
        Self { title }
    }

    #[no_mangle] pub extern "C" fn show(&self) -> String {
        use gtk::prelude::{DialogExt, WidgetExt, WidgetExtManual};
        let dialog = gtk::FileChooserDialog::new::<gtk::Window>(
            Some(self.title),
            None,
            gtk::FileChooserAction::SelectFolder,
        );

        dialog.add_button("Cancel", gtk::ResponseType::Cancel.into());
        dialog.add_button("Open", gtk::ResponseType::Accept.into());

        let mut dir = "".to_string();

        if dialog.run() == gtk::ResponseType::Accept.into() {
            if let path = dialog.path() {
                dir = dialog.path().to_string()
            }
        } else {
            dir = "".to_string()
        }

        unsafe {
            dialog.destroy();
        }

        dir
    }
}
