#include "Win32Window.h"

#include "Win32WindowInterface.h"

#include <WinUser.h>
#include "FileLogger.h"
#include "StringUtils.h"

#include "ui_events\KeyboardEvent.h"
#include "DesktopManager.h"

LRESULT CALLBACK FreeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

Win32Window::Win32Window()
{

}

std::unique_ptr<Win32Window> Win32Window::Create()
{
    return std::make_unique<Win32Window>();
}

HWND Win32Window::GetHandle() const
{
    return mHandle;
}

void Win32Window::CreateNative(Win32ApplicationContext* context, DesktopManager* desktopManager)
{
    mDesktopManager = desktopManager;

    const char CLASS_NAME[] = "Sample Window Class";

    auto hInstance = reinterpret_cast<HINSTANCE>(context->hInstance);
    WNDCLASS wc = { };
    wc.lpfnWndProc = FreeWindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    MLOG_INFO("Request window create");
    mHandle = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        "Media Tool",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

        nullptr,       // Parent window    
        nullptr,       // Menu
        hInstance,  // Instance handle
        this        // Additional application data
    );

    if (mHandle == nullptr)
    {
        LPVOID lpMsgBuf;
        auto dw = ::GetLastError();

        ::FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr, dw,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);

        MLOG_INFO("Request window failed: " << dw);

        LocalFree(lpMsgBuf);
    }
    MLOG_INFO("Request window create got handle: " << "0x" << mHandle);
}

LRESULT CALLBACK Win32Window::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }

        case WM_CHAR:
        {
            auto key_event = std::make_unique<KeyboardEvent>();
            key_event->SetAction(KeyboardEvent::Action::Pressed);

            auto keyChar = (wchar_t)wParam;
            key_event->SetKeyString(StringUtils::convert(std::wstring(1, keyChar)));
            mDesktopManager->OnUiEvent(std::move(key_event));
        }

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(mHandle, &ps);

            FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));

            auto text = L"Hello World";
            auto val = DrawText(hdc, (LPCSTR)(&text[0]), -1, &ps.rcPaint, 0);

            EndPaint(mHandle, &ps);
        }
    }
    return DefWindowProc(mHandle, message, wParam, lParam);
}

static LRESULT CALLBACK FreeWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    Win32Window* window;

    if (uMsg == WM_NCCREATE)
    {
        CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
        window = (Win32Window*)cs->lpCreateParams;

        SetLastError(0);
        if (::SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window) == 0)
        {
            if (GetLastError() != 0)
                return FALSE;
        }
    }
    else
    {
        window = (Win32Window*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
    }

    if (window)
    {
        window->SetHandle(hwnd);
        return window->WindowProc(uMsg, wParam, lParam);
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}