#pragma once
#include "YYCCInternal.hpp"
#if YYCC_OS == YYCC_OS_WINDOWS

#include <memory>

#include "WinImportPrefix.hpp"
#include <Windows.h>
#include <shlobj_core.h>
#include "WinImportSuffix.hpp"

/**
 * @brief COM fucntions related namespace.
 * @details
 * This namespace is Windows specific and is unavailable on other platforms.
 * 
 * This namespace contain a COM Guard which make sure COM was initialized in current module when loading current module.
 * It is essential because all calling to COM functions should be under the premise that COM has been initialized.
 * This guard also will uninitialize COM when unloading this module.
 * 
 * This namespace also provided various memory-safe types for interacting with COM functions.
 * Although Microsoft also has similar smart pointer called \c CComPtr.
 * But this library is eager to hide all Microsoft-related functions calling.
 * Using \c CComPtr is not corresponding with the philosophy of this library.
 * So these std-based smart pointer type were created.
 * 
 * This namespace is used by internal functions as intended.
 * They should not be used outside of this library.
 * But if you compel to use them, it is also okey.
*/
namespace YYCC::COMHelper {

	/**
	 * @brief C++ standard deleter for every COM interfaces inheriting IUnknown.
	*/
	class ComPtrDeleter {
	public:
		ComPtrDeleter() {}
		void operator() (IUnknown* com_ptr) {
			if (com_ptr != nullptr) {
				com_ptr->Release();
			}
		}
	};

	using SmartIFileDialog = std::unique_ptr<IFileDialog, ComPtrDeleter>;
	using SmartIFileOpenDialog = std::unique_ptr<IFileOpenDialog, ComPtrDeleter>;
	using SmartIShellItem = std::unique_ptr<IShellItem, ComPtrDeleter>;
	using SmartIShellItemArray = std::unique_ptr<IShellItemArray, ComPtrDeleter>;
	using SmartIShellFolder = std::unique_ptr<IShellFolder, ComPtrDeleter>;

	/**
	 * @brief C++ standard deleter for almost raw pointer used in COM which need to be free by CoTaskMemFree()
	*/
	class CoTaskMemDeleter {
	public:
		CoTaskMemDeleter() {}
		void operator() (void* com_ptr) {
			if (com_ptr != nullptr) {
				CoTaskMemFree(com_ptr);
			}
		}
	};

	using SmartLPWSTR = std::unique_ptr<std::remove_pointer_t<LPWSTR>, CoTaskMemDeleter>;

	/**
	 * @brief Check whether COM environment has been initialized.
	 * @return True if it is, otherwise false.
	 * @remarks
	 * This function will call corresponding function of COM Guard.
	 * Do not remove this function and you must preserve at least one reference to this function in final program.
	 * Some compiler will try to drop COM Guard in final program if no reference to it and it will cause the initialization of COM environment failed.
	 * This is the reason why I order you do the things said above.
	*/
	bool IsInitialized();

}

#endif