/*****************************************************************************
* Product: Win32 GUI facilities for building realistic embedded front panels
* Last Update: 2015-03-02
*
*                    Q u a n t u m     L e a P s
*                    ---------------------------
*                    innovating embedded systems
*
* Copyright (C) 2002-2012 Quantum Leaps, LLC. All rights reserved.
*
* This program is open source software: you can redistribute it and/or
* modify it under the terms of the following MIT License (MIT).
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Contact information:
* Web  : http://www.state-machine.com
* Email: info@state-machine.com
*****************************************************************************/
#include "win32_gui.h"
#include <stdlib.h>

/*--------------------------------------------------------------------------*/
HWND CreateCustDialog(HINSTANCE hInst, int iDlg, HWND hParent,
                      WNDPROC lpfnWndProc, LPCTSTR lpWndClass)
{
    WNDCLASSEX wndclass;
    HWND       hWnd;

    wndclass.cbSize        = sizeof(wndclass);
    wndclass.style         = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc   = lpfnWndProc;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = DLGWINDOWEXTRA;
    wndclass.hInstance     = hInst;
    wndclass.hIcon         = NULL;
    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)COLOR_WINDOW;
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = lpWndClass;
    wndclass.hIconSm       = NULL;

    RegisterClassEx(&wndclass);

    hWnd = CreateDialog(hInst, MAKEINTRESOURCE(iDlg), hParent, NULL);

    /* NOTE: WM_INITDIALOG provides stimulus for initializing dialog controls.
    * Dialog box procedures typically use this message to initialize controls
    * and carry out any other initialization tasks that affect the appearance
    * of the dialog box.
    */
    SendMessage(hWnd, WM_INITDIALOG, (WPARAM)0, (LPARAM)0);

    return hWnd;
}

/*--------------------------------------------------------------------------*/
void OwnerDrawnButton_init(OwnerDrawnButton * const me,
                           HBITMAP hBitmapUp, HBITMAP hBitmapDwn,
                           HCURSOR hCursor)
{
    me->hBitmapUp   = hBitmapUp;
    me->hBitmapDown = hBitmapDwn;
    me->hCursor     = hCursor;
}
/*..........................................................................*/
void OwnerDrawnButton_xtor(OwnerDrawnButton * const me) {
    DeleteObject(me->hBitmapUp);
    DeleteObject(me->hBitmapDown);
}
/*..........................................................................*/
enum OwnerDrawnButtonAction OwnerDrawnButton_draw(
                               OwnerDrawnButton * const me,
                               LPDRAWITEMSTRUCT lpdis)
{
    enum OwnerDrawnButtonAction ret = BTN_NOACTION;

    if ((lpdis->itemAction & ODA_DRAWENTIRE) != 0U) {
        if (me->hCursor != NULL) {
           SetClassLong(lpdis->hwndItem, GCL_HCURSOR, (LONG)me->hCursor);
           me->hCursor = NULL; /* mark the cursor set */
        }
        DrawBitmap(lpdis->hDC, me->hBitmapUp,
                   lpdis->rcItem.left, lpdis->rcItem.top);
        ret = BTN_PAINTED;
    }
    else if ((lpdis->itemAction & ODA_SELECT) != 0U) {
        if ((lpdis->itemState & ODS_SELECTED) != 0U) {
            DrawBitmap(lpdis->hDC, me->hBitmapDown,
                       lpdis->rcItem.left, lpdis->rcItem.top);
            ret = BTN_DEPRESSED;
        }
        else {
            /* NOTE: the bitmap for button "UP" look will be
            * drawn in the ODA_DRAWENTIRE action
            */
            ret = BTN_RELEASED;
        }
    }
    return ret;
}

/*--------------------------------------------------------------------------*/
void GraphicDisplay_init(GraphicDisplay * const me,
                    UINT width,  UINT xScale,
                    UINT height, UINT yScale,
                    HWND hItem,  BYTE const bgColor[3])
{
    HDC hDC;
    BITMAPINFO bi24BitInfo;

    me->width  = width;
    me->xScale = xScale;
    me->height = height;
    me->yScale = yScale;

    me->bgColor[0] = bgColor[0];
    me->bgColor[1] = bgColor[1];
    me->bgColor[2] = bgColor[2];

    me->hItem = hItem;

    bi24BitInfo.bmiHeader.biBitCount    = 3U*8U;  /* 3 RGB bytes */
    bi24BitInfo.bmiHeader.biCompression = BI_RGB; /* RGB color */
    bi24BitInfo.bmiHeader.biPlanes      = 1U;
    bi24BitInfo.bmiHeader.biSize        = sizeof(bi24BitInfo.bmiHeader);
    bi24BitInfo.bmiHeader.biWidth       = me->width  * me->xScale;
    bi24BitInfo.bmiHeader.biHeight      = me->height * me->yScale;

    hDC = CreateCompatibleDC(NULL);
    me->hBitmap = CreateDIBSection(hDC, &bi24BitInfo, DIB_RGB_COLORS,
                                   (void **)&me->bits, 0, 0);
    DeleteDC(hDC);

    GraphicDisplay_clear(me);
    GraphicDisplay_redraw(me);
}
/*..........................................................................*/
void GraphicDisplay_xtor(GraphicDisplay * const me) {
    DeleteObject(me->hBitmap);
}
/*..........................................................................*/
void GraphicDisplay_clear(GraphicDisplay * const me) {
    UINT n;
    BYTE r = me->bgColor[0];
    BYTE g = me->bgColor[1];
    BYTE b = me->bgColor[2];
    BYTE *bits = me->bits;

    for (n = me->width*me->xScale * me->height * me->yScale;
         n != 0U;
         --n, bits += 3)
    {
        bits[0] = b;
        bits[1] = g;
        bits[2] = r;
    }
}
/*..........................................................................*/
void GraphicDisplay_setPixel(GraphicDisplay * const me, UINT x, UINT y,
                        BYTE const color[3])
{
    UINT sx, sy;
    BYTE *pixelRGB = &me->bits[3*(me->xScale*x
                  + me->xScale*me->width * me->yScale*(me->height - 1U - y))];
    BYTE r = color[0];
    BYTE g = color[1];
    BYTE b = color[2];
    for (sy = me->yScale; sy != 0U;
         --sy, pixelRGB += me->xScale*me->width*3U)
    {
        for (sx = 3U*me->xScale; sx != 0U; sx -= 3U) {
            pixelRGB[sx - 3U] = b;
            pixelRGB[sx - 2U] = g;
            pixelRGB[sx - 1U] = r;
        }
    }
}
/*..........................................................................*/
void GraphicDisplay_clearPixel(GraphicDisplay * const me, UINT x, UINT y) {
    GraphicDisplay_setPixel(me, x, y, me->bgColor);
}
/*..........................................................................*/
void GraphicDisplay_redraw(GraphicDisplay * const me) {
    SendMessage(me->hItem, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)me->hBitmap);
}

/* SegmentDisplay ----------------------------------------------------------*/
void SegmentDisplay_init(SegmentDisplay * const me,
                         UINT segmentNum, UINT bitmapNum)
{
    UINT n;

    me->hSegment = (HWND *)calloc(segmentNum, sizeof(HWND));
    me->segmentNum = segmentNum;
    for (n = 0U; n < segmentNum; ++n) {
        me->hSegment[n] = NULL;
    }
    me->hBitmap = (HBITMAP *)calloc(bitmapNum, sizeof(HBITMAP));
    me->bitmapNum = bitmapNum;
    for (n = 0U; n < bitmapNum; ++n) {
        me->hBitmap[n] = NULL;
    }
}
/*..........................................................................*/
void SegmentDisplay_xtor(SegmentDisplay * const me) {
    UINT n;

    for (n = 0U; n < me->segmentNum; ++n) {
        DeleteObject(me->hSegment[n]);
    }
    free(me->hSegment);

    for (n = 0U; n < me->bitmapNum; ++n) {
        DeleteObject(me->hBitmap[n]);
    }
    free(me->hBitmap);
}
/*..........................................................................*/
BOOL SegmentDisplay_initSegment(SegmentDisplay * const me,
                                UINT segmentNum, HWND hSegment)
{
    if ((segmentNum < me->segmentNum) && (hSegment != NULL)) {
        me->hSegment[segmentNum] = hSegment;
        return TRUE;
    }
    else {
        return FALSE;
    }
}
/*..........................................................................*/
BOOL SegmentDisplay_initBitmap(SegmentDisplay * const me,
                               UINT bitmapNum, HBITMAP hBitmap)
{
    if ((bitmapNum < me->bitmapNum) && (hBitmap != NULL)) {
        me->hBitmap[bitmapNum] = hBitmap;
        return TRUE;
    }
    else {
        return FALSE;
    }
}
/*..........................................................................*/
BOOL SegmentDisplay_setSegment(SegmentDisplay * const me,
                               UINT segmentNum, UINT bitmapNum)
{
    if ((segmentNum < me->segmentNum) && (bitmapNum < me->bitmapNum)) {
        SendMessage(me->hSegment[segmentNum], STM_SETIMAGE,
                IMAGE_BITMAP,
                (LPARAM)me->hBitmap[bitmapNum]);
        return TRUE;
    }
    else {
        return FALSE;
    }
}

/*--------------------------------------------------------------------------*/
/* DrawBitmap() function adapted from the book "Programming Windows" by
* Charles Petzold.
*/
void DrawBitmap(HDC hdc, HBITMAP hBitmap,
                int xStart, int yStart)
{
    BITMAP bm;
    POINT  ptSize, ptOrg;
    HDC    hdcMem = CreateCompatibleDC(hdc);

    SelectObject(hdcMem, hBitmap);
    SetMapMode(hdcMem, GetMapMode(hdc));

    GetObject(hBitmap, sizeof(BITMAP), (LPVOID)&bm);
    ptSize.x = bm.bmWidth;
    ptSize.y = bm.bmHeight;
    DPtoLP(hdc, &ptSize, 1);

    ptOrg.x = 0;
    ptOrg.y = 0;
    DPtoLP(hdcMem, &ptOrg, 1);

    BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y,
           hdcMem, ptOrg.x, ptOrg.y, SRCCOPY);
    DeleteDC(hdcMem);
}


