// +build windows
package win32

foreign import "system:comdlg32.lib"
import "core:strings"

OFN_Hook_Proc :: #type proc "stdcall" (hdlg: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Uint_Ptr;

Open_File_Name_A :: struct {
	struct_size:     u32,
	hwnd_owner:      Hwnd,
	instance:        Hinstance,
	filter:          cstring,
	custom_filter:   cstring,
	max_cust_filter: u32,
	filter_index:    u32,
	file:            cstring,
	max_file:        u32,
	file_title:      cstring,
	max_file_title:  u32,
	initial_dir:     cstring,
	title:           cstring,
	flags:           u32,
	file_offset:     u16,
	file_extension:  u16,
	def_ext:         cstring,
	cust_data:       Lparam,
	hook:            OFN_Hook_Proc,
	template_name:   cstring,
	pv_reserved:     rawptr,
	dw_reserved:     u32,
	flags_ex:        u32,
}

Open_File_Name_W :: struct {
	struct_size:     u32,
	hwnd_owner:      Hwnd,
	instance:        Hinstance,
	filter:          Wstring,
	custom_filter:   Wstring,
	max_cust_filter: u32,
	filter_index:    u32,
	file:            Wstring,
	max_file:        u32,
	file_title:      Wstring,
	max_file_title:  u32,
	initial_dir:     Wstring,
	title:           Wstring,
	flags:           u32,
	file_offset:     u16,
	file_extension:  u16,
	def_ext:         Wstring,
	cust_data:       Lparam,
	hook:            OFN_Hook_Proc,
	template_name:   Wstring,
	pv_reserved:     rawptr,
	dw_reserved:     u32,
	flags_ex:        u32,
}

@(default_calling_convention = "c")
foreign comdlg32 {
	@(link_name="GetOpenFileNameA") get_open_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
	@(link_name="GetOpenFileNameW") get_open_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
	@(link_name="GetSaveFileNameA") get_save_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
	@(link_name="GetSaveFileNameW") get_save_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
	@(link_name="CommDlgExtendedError") comm_dlg_extended_error :: proc() -> u32 ---
}

OPEN_TITLE :: "Select file to open";
OPEN_FLAGS :: u32(OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);
OPEN_FLAGS_MULTI :: OPEN_FLAGS | OFN_ALLOWMULTISELECT | OFN_EXPLORER;

SAVE_TITLE :: "Select file to save";
SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER);
SAVE_EXT   :: "txt";

Open_Save_Mode :: enum {
	Open = 0,
	Save = 1,
}

_open_file_dialog :: proc(title: string, dir: string,
                          filters: []string, default_filter: u32,
                          flags: u32, default_ext: string,
                          mode: Open_Save_Mode, allocator := context.temp_allocator) -> (path: string, ok: bool = true) {
	file_buf := make([]u16, MAX_PATH_WIDE, allocator);

	// Filters need to be passed as a pair of strings (title, filter)
	filter_len := u32(len(filters));
	if filter_len % 2 != 0 do return "", false;

	filter: string;
	filter = strings.join(filters, "\u0000", context.temp_allocator);
	filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator);

	ofn := Open_File_Name_W{
		struct_size  = size_of(Open_File_Name_W),
		file         = Wstring(&file_buf[0]),
		max_file     = MAX_PATH_WIDE,
		title        = utf8_to_wstring(title, context.temp_allocator),
		filter       = utf8_to_wstring(filter, context.temp_allocator),
		initial_dir  = utf8_to_wstring(dir, context.temp_allocator),
		filter_index = u32(clamp(default_filter, 1, filter_len / 2)),
		def_ext      = utf8_to_wstring(default_ext, context.temp_allocator),
		flags        = u32(flags),
	};

	switch mode {
	case .Open:
		ok = bool(get_open_file_name_w(&ofn));
	case .Save:
		ok = bool(get_save_file_name_w(&ofn));
	case:
		ok = false;
	}

	if !ok {
		delete(file_buf);
		return "", false;
	}

	file_name := utf16_to_utf8(file_buf[:], allocator);
	path = strings.trim_right_null(file_name);
	return;
}

select_file_to_open :: proc(title := OPEN_TITLE, dir := ".",
                            filters := []string{"All Files", "*.*"}, default_filter := u32(1),
                            flags := OPEN_FLAGS, allocator := context.temp_allocator) -> (path: string, ok: bool) {

	path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, "", Open_Save_Mode.Open, allocator);
	return;
}

select_file_to_save :: proc(title := SAVE_TITLE, dir := ".",
							filters := []string{"All Files", "*.*"}, default_filter := u32(1),
							flags := SAVE_FLAGS, default_ext := SAVE_EXT,
							allocator := context.temp_allocator) -> (path: string, ok: bool) {

	path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, Open_Save_Mode.Save, allocator);
	return;
}

// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
//       it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you.

OFN_ALLOWMULTISELECT     :: 0x00000200; // NOTE(Jeroen): Without OFN_EXPLORER it uses the Win3 dialog.
OFN_CREATEPROMPT         :: 0x00002000;
OFN_DONTADDTORECENT      :: 0x02000000;
OFN_ENABLEHOOK           :: 0x00000020;
OFN_ENABLEINCLUDENOTIFY  :: 0x00400000;
OFN_ENABLESIZING         :: 0x00800000;
OFN_ENABLETEMPLATE       :: 0x00000040;
OFN_ENABLETEMPLATEHANDLE :: 0x00000080;
OFN_EXPLORER             :: 0x00080000;
OFN_EXTENSIONDIFFERENT   :: 0x00000400;
OFN_FILEMUSTEXIST        :: 0x00001000;
OFN_FORCESHOWHIDDEN      :: 0x10000000;
OFN_HIDEREADONLY         :: 0x00000004;
OFN_LONGNAMES            :: 0x00200000;
OFN_NOCHANGEDIR          :: 0x00000008;
OFN_NODEREFERENCELINKS   :: 0x00100000;
OFN_NOLONGNAMES          :: 0x00040000;
OFN_NONETWORKBUTTON      :: 0x00020000;
OFN_NOREADONLYRETURN     :: 0x00008000;
OFN_NOTESTFILECREATE     :: 0x00010000;
OFN_NOVALIDATE           :: 0x00000100;
OFN_OVERWRITEPROMPT      :: 0x00000002;
OFN_PATHMUSTEXIST        :: 0x00000800;
OFN_READONLY             :: 0x00000001;
OFN_SHAREAWARE           :: 0x00004000;
OFN_SHOWHELP             :: 0x00000010;

CDERR_DIALOGFAILURE      :: 0x0000FFFF;
CDERR_GENERALCODES       :: 0x00000000;
CDERR_STRUCTSIZE         :: 0x00000001;
CDERR_INITIALIZATION     :: 0x00000002;
CDERR_NOTEMPLATE         :: 0x00000003;
CDERR_NOHINSTANCE        :: 0x00000004;
CDERR_LOADSTRFAILURE     :: 0x00000005;
CDERR_FINDRESFAILURE     :: 0x00000006;
CDERR_LOADRESFAILURE     :: 0x00000007;
CDERR_LOCKRESFAILURE     :: 0x00000008;
CDERR_MEMALLOCFAILURE    :: 0x00000009;
CDERR_MEMLOCKFAILURE     :: 0x0000000A;
CDERR_NOHOOK             :: 0x0000000B;
CDERR_REGISTERMSGFAIL    :: 0x0000000C;
