#include "project.h"

#include "resource.h"
#include <signal.h>

#include <CommCtrl.h>

#pragma comment( lib, "comctl32.lib" )

TCHAR txtBuffer[szTxtBuffer + 1];
myUInt WM_SIZE_active = 0;

// Global Variables:
HINSTANCE appHInst;                                // current instance
WCHAR szTitle[MAX_LOADSTRING] = ProjectTitle;
WCHAR szWindowClass[MAX_LOADSTRING] = ProjectClass;
HWND appHWND;

static void signal_handler(int sigType)
{
    if (sigType == SIGSEGV)
    {
        addText((TCHAR *) L"\n*** Invalid segment access ***\n");
    }
    else
    addText((TCHAR*)L"\n*** Non recoverable error\n");

    addText((TCHAR*)L"\nThis program will terminate in about 10 seconds.\n");

    Sleep(10000);
    exit(sigType);
}

// --------------------------------------------------------------------------------------------------------------------------------------

static LRESULT  WndProcOrig(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case 0:  // to avoid a compiler WARNING
            default:
            return DefWindowProc(hWnd, message, wParam, lParam);
            break;
    }
    return 0;
}

LRESULT(*msgProc[WM_USER])  (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

// --------------------------------------------------------------------------------------------------------------------------------------
#define AppMsgCount 32

int AppMsgUsed = 0;

struct
{
    UINT msgNumber;
    MESSAGEHANDLER msgProc;

} msgApp[AppMsgCount];

static void init_appMessage()
{
    int index = 0;
    for (index = 0; index < AppMsgCount; index++)
    {
        msgApp[index].msgNumber = 0;
        msgApp[index].msgProc = (MESSAGEHANDLER)&WndProcOrig;
    }
    AppMsgUsed = 0;

}

LRESULT  add_AppMessage(UINT  msgNumber, MESSAGEHANDLER cbFnc )
{

    if (!(AppMsgUsed < AppMsgCount)) return 0;

    int index = 0;

    for (index = 0; index < AppMsgCount; index++)
    {
        if (!msgApp[index].msgNumber)
        {
            msgApp[index].msgNumber = msgNumber;
            msgApp[index].msgProc = cbFnc;
            AppMsgUsed++;
            return msgNumber;
        }
    }
    return 0;

}
// --------------------------------------------------------------------------------------------------------------------------------------

static LRESULT WM_CLOSE_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return msg_handled_by_application;
}

static LRESULT WM_INITMENUPOPUP_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    SOBID  sob_index = sob_find_hmenu((HMENU)wParam);

    if (sob_index NEQU SOBID_ERROR)
    {
        EVENTHANDLER eventhandler = sob_get_EVENTHANDLER(sob_index);

        if (eventhandler)
        {
            eventhandler(hWnd, message, wParam, lParam ,sob_get_base(sob_index) );
            return msg_handled_by_application;
        }
    }

    return msg_handled_by_application;
}

static LRESULT WM_CONTEXTMENU_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return msg_handled_by_application;
}

static LRESULT WM_COMMAND_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // rules
    // Message Source 	wParam (high word) 	                    wParam (low word) 	                lParam
    //         Menu     0 	                                    Menu identifier(IDM_*) 	            0
    //   Accelerator 	1 	                                    Accelerator identifier(IDM_*) 	    0
    //  Control 	    Control - defined notification code 	Control identifier 	                Handle to the control window
    // -----------------------------------------------------

    SOBDATA* sData = sob_find_hwnd((HWND)lParam);
    if (sData EQU NULL)  return msg_handled_by_application;

    // note: menu clicks get reported as the MENUBAR HWND
    // SO DO NOT GIVE THE MENUBAR    an    EVENTHANDLER

    EVENTHANDLER eventhandler = sData->eventHandler;
    if (eventhandler)
    {
        eventhandler(hWnd, message, wParam, (LPARAM)sData, sData);
        return msg_handled_by_application;
    }

    // -----------------------------------------------------
    // must be a MENU / Accelerator notification

    int wmId = LOWORD(wParam);
    int hiw = HIWORD(wParam);

    eventhandler = sob_get_EVENTHANDLER((SOBID)wmId);

    if (eventhandler)
    {
        // swprintf_s(txtBuffer, szTxtBuffer, L" EVENTHANDLER  0x%04x   0x%llx  0x%llx \n", message ,(long long)wmId, (myUInt)sData); addText(txtBuffer);

        eventhandler(hWnd, message, wParam, lParam, sob_get_base ( (SOBID)wmId) );
        return msg_handled_by_application;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

static LRESULT WM_DESTROY_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    inform_modules(SS_POWER_DOWN);
    PostQuitMessage(0);

    return msg_handled_by_application;
}

static LRESULT WM_PAINT_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    if ( sobDisplaying)  return  WndProcOrig(hWnd, message, wParam, lParam);

    sobDisplaying = 1;
    SOBDATA* sData = sob_find_hwnd(hWnd);
    if (sData)
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

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

        EndPaint(hWnd, &ps);
        sobDisplaying = 0;

        return msg_handled_by_application;
    }

    sobDisplaying =0;
    return msg_handled_by_application;
}

static LRESULT WM_DRAWITEM_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    SOBDATA*  sData =  sob_find_hwnd(((LPDRAWITEMSTRUCT)lParam)->hwndItem);
    if (sData)
    {
        if (sData->ownerDrawnHandler)
        {
            sData->ownerDrawnHandler(sData , (LPDRAWITEMSTRUCT)lParam );
            return TRUE;
        }
    }
    return FALSE;
}

static LRESULT WM_EXITSIZEMOVE_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    // is NOT sent for a MINIMIZE or MAXIMIZE action

    SOBDATA* sData = sob_find_hwnd(hWnd);
    if (sData EQU NULL)  return msg_handled_by_application;

    WM_SIZE_active = 0;
    long w, h , wBnd , hBnd ;

    if (sData->subType EQU SOB_CONTAINER_WINDOW)
    {
        //        debug((TCHAR*)_TEXT("WM_EXITSIZEMOVE_handler"));
        WINDOWINFO myWI = {0};
        myWI.cbSize = sizeof(WINDOWINFO);
        GetWindowInfo(sData->hWnd, &myWI);

        w = myWI.rcWindow.right - myWI.rcWindow.left;
        h = myWI.rcWindow.bottom - myWI.rcWindow.top;

        wBnd = abs((myWI.rcWindow.right - myWI.rcWindow.left) - (myWI.rcClient.right - myWI.rcClient.left));
        hBnd = abs((myWI.rcWindow.bottom - myWI.rcWindow.top) - (myWI.rcClient.bottom - myWI.rcClient.top));

        if ((sData->displayHeight NEQU (h-hBnd) )  LOR (sData->displayWidth  NEQU (w-wBnd)  )  )
        {

            // the minHeight and the midWidth must be observed
            if (( h - hBnd) >  sData->minHeight)   sData->displayHeight = h - hBnd;
            else sData->displayHeight = sData->minHeight  ;

            if ((w - wBnd)  >  sData->minWidth)   sData->displayWidth = w  - wBnd  ;
            else sData->displayWidth = sData->minWidth;

            //    return  WndProcOrig(hWnd, message, wParam, lParam);

            sData->pVtbl->Display(sData);
        }

    };

    //    sData->ppSobData->pVtbl->(pData);

    return msg_handled_by_application;
}
static LRESULT WM_MOVING_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    SOBDATA* sData = sob_find_hwnd((HWND)lParam);
    if (sData EQU NULL)  return msg_handled_by_application;

    //   RECT winRECT;
    WM_SIZE_active = 0;
    debug((TCHAR*)_TEXT("MOVING")); debugInt(sData->sIndex);

    return   msg_not_handled_by_application;

}
static LRESULT WM_SIZING_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    SOBDATA* sData = sob_find_hwnd(hWnd);
    if (sData EQU NULL)  return msg_handled_by_application;

    //   RECT winRECT;
    WM_SIZE_active = 0;
    debug((TCHAR*)_TEXT("SIZING")); debugInt(sData->sIndex);

    return   msg_not_handled_by_application;

}

static LRESULT WM_WINDOWPOSCHANGED_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    SOBDATA* sData = sob_find_hwnd(hWnd);

    if (sData EQU NULL)  return DefWindowProc(hWnd, message, wParam, lParam);

    //  BOOL IsIconic(HWND)

    //   RECT winRECT;
    WM_SIZE_active = 0;

    //   debug((TCHAR*)_TEXT("WM_WINDOWPOSCHANGED_handler"));

    if (sData->subType EQU SOB_CONTAINER_WINDOW)
    {

        //        InvalidateRect(sData->hWnd, (RECT*)0, TRUE);
        WINDOWINFO myWI = {0};
        myWI.cbSize = sizeof(WINDOWINFO);
        GetWindowInfo(sData->hWnd, &myWI);
        long w, h, wBnd, hBnd;

        w = myWI.rcWindow.right - myWI.rcWindow.left;
        h = myWI.rcWindow.bottom - myWI.rcWindow.top;

        wBnd = abs((myWI.rcWindow.right - myWI.rcWindow.left) - (myWI.rcClient.right - myWI.rcClient.left));
        hBnd = abs((myWI.rcWindow.bottom - myWI.rcWindow.top) - (myWI.rcClient.bottom - myWI.rcClient.top));

        if ((sData->displayHeight NEQU(h - hBnd))  LOR(sData->displayWidth  NEQU(w - wBnd)))
        {
            debug((TCHAR*)_TEXT("WM_WINDOWPOSCHANGED_handler"));

            // the minHeight and the midWidth must be observed
            if ((h - hBnd) > sData->minHeight)   sData->displayHeight = h - hBnd;
            else sData->displayHeight = sData->minHeight;

            if ((w - wBnd) > sData->minWidth)   sData->displayWidth = w - wBnd;
            else sData->displayWidth = sData->minWidth;

            //    return  WndProcOrig(hWnd, message, wParam, lParam);
            sData->pVtbl->UpDateChildren(sData);

            InvalidateRect(sData->hWnd, (RECT*)0, TRUE);
            sData->pVtbl->Display(sData);
            UpdateWindow(hWnd);
        }

    };

    return   msg_handled_by_application;
    return DefWindowProc(hWnd, message, wParam, lParam);

}

static LRESULT WM_SIZE_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // need this to catch  MAXIMIZE and MINIMIZE actions

    SOBDATA* sData = sob_find_hwnd(hWnd);

    if (sData EQU NULL)  return DefWindowProc(hWnd, message, wParam, lParam);

    //  BOOL IsIconic(HWND)

    //   RECT winRECT;
    WM_SIZE_active = 0;

    //   debug((TCHAR*)_TEXT("WM_SIZE_handler"));

    if (sData->subType EQU SOB_CONTAINER_WINDOW)
    {
        //        InvalidateRect(sData->hWnd, (RECT*)0, TRUE);
     //   debug((TCHAR*)_TEXT("WM_SIZE_handler"));
        WINDOWINFO myWI = {0};
        myWI.cbSize = sizeof(WINDOWINFO);
        GetWindowInfo(sData->hWnd, &myWI);
        long w, h, wBnd, hBnd;

        w = myWI.rcWindow.right - myWI.rcWindow.left;
        h = myWI.rcWindow.bottom - myWI.rcWindow.top;

        wBnd = abs((myWI.rcWindow.right - myWI.rcWindow.left) - (myWI.rcClient.right - myWI.rcClient.left));
        hBnd = abs((myWI.rcWindow.bottom - myWI.rcWindow.top) - (myWI.rcClient.bottom - myWI.rcClient.top));

        if ((sData->displayHeight NEQU(h - hBnd))  LOR(sData->displayWidth  NEQU(w - wBnd)))
        {

            // the minHeight and the midWidth must be observed
            if ((h - hBnd) > sData->minHeight)   sData->displayHeight = h - hBnd;
            else sData->displayHeight = sData->minHeight;

            if ((w - wBnd) > sData->minWidth)   sData->displayWidth = w - wBnd;
            else sData->displayWidth = sData->minWidth;

            //    return  WndProcOrig(hWnd, message, wParam, lParam);
            sData->pVtbl->UpDateChildren(sData);

            InvalidateRect(sData->hWnd, (RECT*)0, TRUE);
            sData->pVtbl->Display(sData);
            UpdateWindow(hWnd);
        }

    };

    //   return   msg_handled_by_application;
    return DefWindowProc(hWnd, message, wParam, lParam);

}

static LRESULT WM_CTLCOLORBTN_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc = (HDC)wParam;
    HWND hwnd = (HWND)lParam;

    if ( 0  )
    {

        HBRUSH  hButtonBkg = CreateSolidBrush(RGB(200, 0, 200));
        SetTextColor(hdc, RGB(0,255,0));
        return (LRESULT)hButtonBkg;
    }

    return msg_handled_by_application;
}

static LRESULT WM_CTLCOLxxx_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC hdc = (HDC)wParam;
    HWND hwnd = (HWND)lParam;
    if (0)
    {
        SetBkColor(hdc, RGB(255, 200, 200));
        SetDCBrushColor(hdc, RGB(255, 200, 200));
        return   (LRESULT)GetStockObject(DC_BRUSH); // return a DC brush.
    }

    return msg_handled_by_application;
}

static LRESULT WM_DO_NOTHING_handler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    return msg_handled_by_application;
}

int register_msg_handler(int msgId, LRESULT (*cbFnc)  (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)   )
{
    if (msgId < WM_USER )  msgProc[msgId] = cbFnc;
    return 1;
}

static void msgProc_INIT()
{
    myUInt index;

    init_appMessage();

    for (index = 0; index < WM_USER; index++)
    {
        msgProc[index] = &WndProcOrig;
    }

    //     msgProc[WM_CREATE] = &WM_CREATE_handler;
    //   msgProc[WM_CLOSE] = &WM_CLOSE_handler;
    msgProc[WM_DRAWITEM] = &WM_DRAWITEM_handler;
    msgProc[WM_DESTROY] = &WM_DESTROY_handler;
    msgProc[WM_SIZE] = &WM_SIZE_handler;

    //  msgProc[WM_WINDOWPOSCHANGED] = &WM_WINDOWPOSCHANGED_handler;

    msgProc[WM_CONTEXTMENU] = &WM_CONTEXTMENU_handler;
    // msgProc[WM_DEVICECHANGE] = &WM_DEVICECHANGE_handler;

    // msgProc[MM_WOM_DONE] = &MM_WOM_DONE_handler;
    // msgProc[MM_WIM_DATA] = &MM_WIM_DATA_handler;

    // msgProc[WM_ERASEBKGND] = &WM_ERASEBKGND_handler;
    // msgProc[WM_KEYDOWN] = &WM_KEYDOWN_handler;

    msgProc[WM_PAINT] = &WM_PAINT_handler;

    // msgProc[WM_LBUTTONDBLCLK] = &WM_LBUTTONDBLCLK_handler;

    // msgProc[WM_LBUTTONDOWN] = &WM_LBUTTONDOWN_handler;
    // msgProc[WM_LBUTTONUP] = &WM_LBUTTONUP_handler;
    // msgProc[WM_MBUTTONDBLCLK] = &WM_MBUTTONDBLCLK_handler;
    // msgProc[WM_MBUTTONDOWN] = &WM_MBUTTONDOWN_handler;
    // msgProc[WM_MBUTTONUP] = &WM_MBUTTONUP_handler;
    // msgProc[WM_RBUTTONDBLCLK] = &WM_RBUTTONDBLCLK_handler;
    // msgProc[WM_RBUTTONDOWN] = &WM_RBUTTONDOWN_handler;
    // msgProc[WM_RBUTTONUP] = &WM_RBUTTONUP_handler;
    // msgProc[WM_MOUSEMOVE] = &WM_MOUSEMOVE_handler;

    // msgProc[WM_CTLCOLORSTATIC] = &WM_CTLCOLxxx_handler;
    msgProc[WM_CTLCOLORBTN] = &WM_CTLCOLORBTN_handler;
    // msgProc[WM_CTLCOLORLISTBOX] = &WM_CTLCOLxxx_handler;
    // msgProc[WM_CTLCOLORSCROLLBAR] = &WM_CTLCOLxxx_handler;
    msgProc[WM_CTLCOLOREDIT] = &WM_CTLCOLxxx_handler;

    // msgProc[WM_ENTERSIZEMOVE] = &WM_ENTERSIZEMOVE_handler;
    msgProc[WM_EXITSIZEMOVE] = &WM_EXITSIZEMOVE_handler;

    //    msgProc[WM_SIZING] = &WM_SIZING_handler;
    //      msgProc[WM_MOVING] = &WM_MOVING_handler;
    // msgProc[WM_WINDOWPOSCHANGING] = &WM_WINDOWPOSCHANGING_handler;

    msgProc[WM_COMMAND] = &WM_COMMAND_handler;

    // msgProc[WM_SYSCOMMAND] = &WM_SYSCOMMAND_handler;

    msgProc[WM_INITMENUPOPUP] = &WM_INITMENUPOPUP_handler;
    // msgProc[WM_MENUCOMMAND] = &WM_MENUCOMMAND_handler;
    // msgProc[WM_MENUSELECT] = &WM_MENUSELECT_handler;

    // msgProc[WM_KILLFOCUS] = &WM_KILLFOCUS_handler;
    // msgProc[WM_SETFOCUS] = &WM_SETFOCUS_handler;

    // msgProc[WM_VSCROLL] = &WM_VSCROLL_handler;
    // msgProc[WM_HSCROLL] = &WM_HSCROLL_handler;

    // msgProc[MM_WIM_OPEN] = &WM_DO_NOTHING_handler;
    // msgProc[MM_WIM_CLOSE] = &WM_DO_NOTHING_handler;

    // msgProc[WM_MEASUREITEM] = &WM_DO_NOTHING_handler;
    // msgProc[WM_MEASUREITEM] = &WM_MEASUREITEM_handler;

    // msgProc[MM_WOM_OPEN] = &WM_DO_NOTHING_handler;
    // msgProc[MM_WOM_CLOSE] = &WM_DO_NOTHING_handler;

    // msgProc[WM_SHOWWINDOW] = &WM_DO_NOTHING_handler;

    // msgProc[WM_MOUSEWHEEL] = &WM_DO_NOTHING_handler;
    // msgProc[WM_MOUSEACTIVATE] = &WM_DO_NOTHING_handler;

    // msgProc[WM_MOVING] = &WM_DO_NOTHING_handler;
    // msgProc[WM_GETMINMAXINFO] = &WM_DO_NOTHING_handler;
    // msgProc[WM_DROPFILES] = &WM_DO_NOTHING_handler;

}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT retCode;

    if (message < WM_USER)
    {
        retCode = (*msgProc[message]) (hWnd, message, wParam, lParam);
    }
    else if (message < (WM_APP))
    {
        retCode = WndProcOrig(hWnd, message, wParam, lParam);
    }
    else if (message < (0xBFFF))
    {

        if ( !AppMsgUsed ) retCode = WndProcOrig(hWnd, message, wParam, lParam);

        int index = 0;
        for (index = 0; index < AppMsgUsed; index++)
        { 
            if ( msgApp[index].msgNumber EQU message)
            {
                retCode = (*msgApp[index].msgProc) (hWnd, message, wParam, lParam);
            }
        }
        retCode = WndProcOrig(hWnd, message, wParam, lParam);
    }
    else
    {
        retCode = WndProcOrig(hWnd, message, wParam, lParam);
    }

    return retCode;
}

//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
static ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex = { 0 };

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APROG));
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL; //  MAKEINTRESOURCEW(IDC_APROG);
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   FUNCTION: InitInstance(HINSTANCE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
static BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
    appHInst = hInstance; // Store instance handle in our global variable
    signal(SIGSEGV, signal_handler);
    signal(SIGABRT, signal_handler);

    msgProc_INIT();  // IMPORTANT before any messages can be received!

    // Initialize common controls
    INITCOMMONCONTROLSEX icex;
    icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
    icex.dwICC = ICC_STANDARD_CLASSES;
    InitCommonControlsEx(&icex);

    (void) initApplication();

    return TRUE;
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.

    MyRegisterClass(hInstance);

    // Perform application initialization:
    if (!InitInstance(hInstance, nCmdShow))
    {
        return FALSE;
    }

    MSG msg;

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

