#include <cstdint>
//#define CINTERFACE
//#define COBJMACROS
// PSAPI was updated to v2 with W7, this makes it compatible with Vista
#define PSAPI_VERSION 1
#pragma comment(lib, "psapi.lib")

#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include <Shobjidl.h>
#include <string.h>
#include <stdio.h>
#include <psapi.h>
#include <type_traits>
#include <time.h>

void dumpbytes(void* ptr, size_t len) {
	for (size_t i = 0; i < len; ++i) {
		printf("%02X ", *((uint8_t*)ptr + i));
	}
	printf("\n");
}

enum FileDialogPrivateOptions {
	FDPO_DONT_FORCE_SHOW_EXTS = 16
};

struct IFileDialogPrivate_Vista {
	IFileDialogPrivate_VistaVtbl* lpVtbl;
};
struct IFileDialogPrivate_VistaVtbl {
	HRESULT(*QueryInterface)(IFileDialogPrivate_Vista*, IID*, void**);
	ULONG(*AddRef)(IFileDialogPrivate_Vista*);
	ULONG(*Release)(IFileDialogPrivate_Vista*);
	HRESULT(*GetPrivateOptions)(IFileDialogPrivate_Vista*, FileDialogPrivateOptions*);
	HRESULT(*SetPrivateOptions)(IFileDialogPrivate_Vista*, FileDialogPrivateOptions);
};

struct IFileDialogPrivate_W10 {
	IFileDialogPrivate_W10Vtbl* lpVtbl;
};
struct IFileDialogPrivate_W10Vtbl {
	HRESULT(*QueryInterface)(IFileDialogPrivate_W10*, IID*, void**);
	ULONG(*AddRef)(IFileDialogPrivate_W10*);
	ULONG(*Release)(IFileDialogPrivate_W10*);
	HRESULT(*HideControlsForHostedPickerProviderApp)(IFileDialogPrivate_W10*);
	HRESULT(*EnableControlsForHostedPickerProviderApp)(IFileDialogPrivate_W10*);
	HRESULT(*GetPrivateOptions)(IFileDialogPrivate_W10*, FileDialogPrivateOptions*);
	HRESULT(*SetPrivateOptions)(IFileDialogPrivate_W10*, FileDialogPrivateOptions);
};

#define INITGUID
#include <initguid.h>
DEFINE_GUID(IID_IFileDialogPrivate_Vista, 0x2539E31C, 0x857F, 0x43C4, 0x88, 0x72, 0x45, 0xBD, 0x6A, 0x02, 0x48, 0x92);
DEFINE_GUID(IFileDialogPrivate_W7, 0xAC92FFC5, 0xF0E9, 0x455A, 0x90, 0x6B, 0x4A, 0x83, 0xE7, 0x4A, 0x80, 0x3B);
DEFINE_GUID(IID_IFileDialogPrivate_W10, 0x9EA5491C, 0x89C8, 0x4BEF, 0x93, 0xD3, 0x7F, 0x66, 0x5F, 0xB8, 0x2A, 0x33);
//DEFINE_GUID(IID_IFileDialog, 0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 0x5d, 0x13, 0x5f, 0xc8);

int main()
{
#if 1
	COMDLG_FILTERSPEC ComDlgFS[3] = {{L"C++ code files", L"*.cpp;*.h;*.rc"},{L"Executable Files", L"*.exe;*.dll"}, {L"All Files",L"*.*"}};
	IFileOpenDialog* pFileOpen = NULL;
	IShellItem* pShellItem = NULL;
	wchar_t* ppszName = NULL;

	CoInitialize(NULL);
	if (CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void**)(&pFileOpen)) != S_OK) {
		return 0;
	}
#if 0
	HMODULE comdlg32_handle = GetModuleHandleA("C:\\Windows\\system32\\comdlg32.dll");
	MODULEINFO comdlg32_info;
	GetModuleInformation(GetCurrentProcess(), comdlg32_handle, &comdlg32_info, sizeof(MODULEINFO));
	printf("FileOpenDialog::SetFileTypes @ comdlg32+0x%llX %08llX\n", (uintptr_t)pFileOpen->lpVtbl->SetFileTypes - (uintptr_t)comdlg32_info.lpBaseOfDll, (uintptr_t)pFileOpen->lpVtbl->SetFileTypes);
	dumpbytes(pFileOpen->lpVtbl->SetFileTypes, 24);
	printf("FileOpenDialog::Show @ comdlg32+0x%llX %08llX\n", (uintptr_t)pFileOpen->lpVtbl->Show - (uintptr_t)comdlg32_info.lpBaseOfDll, (uintptr_t)pFileOpen->lpVtbl->Show);
	dumpbytes(pFileOpen->lpVtbl->Show, 24);
	printf("IFileDialog::SetFileTypes @ vtable+0x%llX\n", offsetof(std::remove_reference_t<decltype(*pFileOpen->lpVtbl)>, SetFileTypes));

	getchar();
#endif
	pFileOpen->SetFileTypes(3, ComDlgFS);
	pFileOpen->SetTitle(L"A Single Selection Dialog");

	IFileDialogPrivate_W10 *w10 = nullptr;
	IFileDialogPrivate_Vista *vista = nullptr;
	if (pFileOpen->QueryInterface(IID_IFileDialogPrivate_W10, (void**)(&w10)) == S_OK) {
		FileDialogPrivateOptions flags;
		w10->lpVtbl->GetPrivateOptions(w10, &flags);
		printf("w10 flags = %08lX\n", *((uint32_t*)(&flags)));
		printf("Has hide? %s\n", (flags & FDPO_DONT_FORCE_SHOW_EXTS) ? "yes" : "no");


		flags = static_cast<FileDialogPrivateOptions>(flags | FDPO_DONT_FORCE_SHOW_EXTS);
		printf("Set hide\n");
		w10->lpVtbl->SetPrivateOptions(w10, flags);
		w10->lpVtbl->GetPrivateOptions(w10, &flags);
		printf("w10 flags = %08lX\n", *((uint32_t*)(&flags)));
		printf("Has hide? %s\n", (flags & FDPO_DONT_FORCE_SHOW_EXTS) ? "yes" : "no");

		w10->lpVtbl->Release(w10);
	}
	else if (pFileOpen->QueryInterface(IID_IFileDialogPrivate_Vista, (void**)(&vista)) == S_OK) {
		FileDialogPrivateOptions flags;
		vista->lpVtbl->GetPrivateOptions(vista, &flags);
		printf("Vista flags = %08lX\n", *((uint32_t*)(&flags)));
		printf("Has hide? %s\n", (flags & FDPO_DONT_FORCE_SHOW_EXTS) ? "yes" : "no");


		flags = static_cast<FileDialogPrivateOptions>(flags | FDPO_DONT_FORCE_SHOW_EXTS);
		printf("Set hide\n");
		vista->lpVtbl->SetPrivateOptions(vista, flags);
		vista->lpVtbl->GetPrivateOptions(vista, &flags);
		printf("Vista flags = %08lX\n", *((uint32_t*)(&flags)));
		printf("Has hide? %s\n", (flags & FDPO_DONT_FORCE_SHOW_EXTS) ? "yes" : "no");

		vista->lpVtbl->Release(vista);
	}
	else {
		printf("No private API :(\n");
	}

	while (true) {
		pFileOpen->Show(GetConsoleWindow());
		Sleep(1000);
	}
	//pFileOpen->GetResult(&pShellItem);
	//pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &ppszName);
	//wprintf(L"%s\n", ppszName);
	//CoTaskMemFree(ppszName);
	//pShellItem->Release();
	pFileOpen->Release();

	pFileOpen = NULL;
	pShellItem = NULL;
	ppszName = NULL;

	CoUninitialize();
#endif

#if 0
	OPENFILENAMEA ofn;
	char pFilePath[256];
	char pFileTitle[256];

	// Setup of OPENFILENAME structure
	ofn.lStructSize = sizeof(OPENFILENAMEA);
	ofn.hwndOwner = NULL;
	ofn.hInstance = NULL;
	ofn.lpstrFilter = "Shown (*.abc)\0*.abc\0Hidden\0*.def\0\0";
	ofn.lpstrCustomFilter = (LPSTR)NULL;
	ofn.nMaxCustFilter = 0;
	ofn.nFilterIndex = 1;
	pFilePath[0] = '\0';
	ofn.lpstrFile = pFilePath;
	ofn.nMaxFile = sizeof(pFilePath);
	ofn.lpstrFileTitle = pFileTitle;
	ofn.nMaxFileTitle = sizeof(pFileTitle);
	ofn.lpstrInitialDir = NULL;
	ofn.lpstrTitle = "Select the export directory";
	ofn.Flags = (OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST);
	ofn.lpstrDefExt = "DLL";
	ofn.nFileOffset = 0;
	ofn.nFileExtension = 0;
	ofn.lCustData = 0;

	// Invoke the file open common dialog
	GetOpenFileNameA(&ofn);

	getchar();
#endif

	return 0;
}