pub mod com;
pub mod dpi;
pub mod gdi;
pub mod handle;
pub mod hr;
pub mod module;
pub mod process;


pub use crate::os::win::com::{iid, ComPtr, Iid};
pub use crate::os::win::gdi::GdiObject;
pub use crate::os::win::handle::WindowsHandle;
pub use crate::os::win::module::{Library, Symbol};


use crate::os::win::dpi::activate_dpi_awareness;
use std::ffi::{OsStr, OsString};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use winapi::shared::minwindef::{DWORD, TRUE};
use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
use winapi::um::errhandlingapi::SetErrorMode;
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::{SEM_FAILCRITICALERRORS, SEM_NOGPFAULTERRORBOX, SEM_NOOPENFILEERRORBOX, STD_OUTPUT_HANDLE};


crate fn initialize() {
    disable_wer();
    enable_vt();

    activate_dpi_awareness();
}


fn disable_wer() {
    unsafe {
        if !cfg!(debug_assertions) {
            SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
        }
    }
}

fn enable_vt() -> bool {
    // not present in `crate winapi`.
    const ENABLE_VIRTUAL_TERMINAL_PROCESSING: DWORD = 0x0004;

    unsafe {
        let handle = GetStdHandle(STD_OUTPUT_HANDLE);

        if handle != INVALID_HANDLE_VALUE {
            let mut mode: DWORD = 0;

            return GetConsoleMode(handle, &mut mode) == TRUE
                && SetConsoleMode(handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) == TRUE;
        }

        false
    }
}


// :: string-related methods.

pub fn to_utf16(string: impl AsRef<OsStr>) -> Vec<u16> {
    string.as_ref().encode_wide().collect()
}

pub fn to_utf16_null(string: impl AsRef<OsStr>) -> Vec<u16> {
    string.as_ref().encode_wide().chain(Some(0)).collect()
}


pub fn from_utf16(data: &[u16]) -> OsString {
    OsString::from_wide(data)
}

pub fn from_utf16_null(data: &[u16]) -> OsString {
    let length = data.iter().position(|x| *x == 0).unwrap_or(data.len());

    OsString::from_wide(&data[..length])
}


pub unsafe fn from_utf16_ptr(data: *const u16, length: usize) -> OsString {
    assert![!data.is_null()];

    let slice = std::slice::from_raw_parts(data, length);
    OsString::from_wide(slice)
}

pub unsafe fn from_utf16_ptr_null(data: *const u16) -> OsString {
    assert![!data.is_null()];

    let length = (0..std::isize::MAX)
        .position(|i| *data.offset(i) == 0)
        .expect("data must be null terminated");

    let slice = std::slice::from_raw_parts(data, length);

    OsString::from_wide(slice)
}