/*
 * w_common.c : Common routines for NetHack windows
 *
 * Copyright (c) Yukihiko Aoki 1999, 2005
 * NetHack may be freely redistributed.  See license for details.
 *
 */

#include "hack.h"

#ifdef NH2K_EXTENDS

#include "win32api.h"
#include "w_main.h"

/*
 * Local variables
 */
static WINDESC *windowList = NULL;
static int childId = 100;

/*
 * Local functions
 */
static void FDECL(DrawMore, (WINDESC *,HDC));
static void FDECL(NHWnd_addDesc, (WINDESC*));
static void FDECL(NHWnd_delDesc, (WINDESC*));
static void CALLBACK FDECL(MoreTimerProc, (HWND,UINT,UINT,DWORD));

/***************************************************************************************
 * 
 ***************************************************************************************/
void NHWnd_show(WINDESC *wd, BOOL bShow)
{
    if (NHWnd_isValid(wd)) {
        if (bShow) {
            ShowWindow(wd->hwnd, SW_SHOW);
        } else {
            ShowWindow(wd->hwnd, SW_HIDE);
        }
    }
}

/***************************************************************************************
 * Save current settings to ini file
 ***************************************************************************************/
void NHWnd_saveProperty(char *section, void *property, int len)
{
    WritePrivateProfileStruct(
        section, "Property", property, len, g_propFile);
}

/***************************************************************************************
 * Load settings from ini file 
 ***************************************************************************************/
BOOL NHWnd_loadProperty(char *section, void *property, int len, void (*defaultProc)(void *))
{
    BOOL result;

    result = GetPrivateProfileStruct(section, "Property", property, len, g_propFile);
    if (!result && defaultProc) {
        defaultProc(property);
    }

    return result;
}

/***************************************************************************************
 * Center window
 ***************************************************************************************/
void NHWnd_moveCenter(HWND hwndChild, HWND hwndParent)
{
    POINT pt_indent;
    RECT  rc_child;
    RECT  rc_parent;

    GetWindowRect(hwndChild, &rc_child);
    GetClientRect(hwndParent, &rc_parent);

    pt_indent.x = 
        ((rc_parent.right - rc_parent.left) - (rc_child.right - rc_child.left)) / 2;
    pt_indent.y = 
        ((rc_parent.bottom - rc_parent.top) - (rc_child.bottom - rc_child.top)) / 2;

    SetWindowPos(
        hwndChild,
        NULL,
        pt_indent.x,
        pt_indent.y,
        0, 0,
        SWP_NOZORDER|SWP_NOSIZE) ;
}

/***************************************************************************************
 * WM_CREATE or WM_NCCREATE
 ***************************************************************************************/
BOOL NHWnd_OnCreate(HWND hwnd,LPCREATESTRUCT lpCS)
{
    SetWindowLong(hwnd, GWL_USERDATA, (LONG)lpCS->lpCreateParams);
    return TRUE;
}

/***************************************************************************************
 * WM_PAINT
 ***************************************************************************************/
void NHWnd_OnPaint(HWND hwnd)
{
    WINDESC *wd = GETWINDESC(hwnd);

    ValidateRect(hwnd, NULL);
    if (NHWnd_isValid(wd)) {
        NHWnd_display(wd);
    }
}

/***************************************************************************************
 * WM_ERASEBKGND
 ***************************************************************************************/
BOOL NHWnd_OnEraseBkgnd(HWND hwnd, HDC hdc)
{
    return FALSE;
}

/***************************************************************************************
 * Flicker free display routine
 ***************************************************************************************/
void NHWnd_display(WINDESC *wd)
{
    HDC      hdc, dcMem;
    HBITMAP  bmp, oldBmp;
    HPALETTE oldPal;
    RECT     rc;
    POINT    pt;

    /* Get device context */
    hdc = GetDC(wd->hwnd);

    if (wd->draw_memory && wd->procDraw) {
        dcMem = CreateCompatibleDC(hdc);
        GetClipBox(hdc, &rc);
        bmp = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
        oldBmp = SelectObject(dcMem, bmp);
        oldPal = SelectPalette(dcMem, disp_procs.getPalette(), FALSE);
        SetWindowOrgEx(dcMem, rc.left, rc.top, &pt);
        wd->procDraw(wd, dcMem);
        BitBlt(hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
            dcMem, rc.left, rc.top, SRCCOPY);
        SetWindowOrgEx(dcMem, pt.x, pt.y, NULL);
        SelectPalette(dcMem, oldPal, FALSE);
        SelectObject(dcMem, oldBmp);
        DeleteObject(bmp);
        DeleteDC(dcMem);
    } else if (wd->procDraw) {
        wd->procDraw(wd, hdc);
    }
    DrawMore(wd, hdc);

    /* Release device context */
    ReleaseDC(wd->hwnd, hdc);

}

/*-------------------------------------------------------------------------------------
 * Draw more icon at bottom-right of window
 *-------------------------------------------------------------------------------------*/
static void DrawMore(WINDESC *wd, HDC hdc)
{
    static int more_id = 0;
    RECT rc;
    HDC dcMem;
    HBITMAP oldBmp;

    if (!wd->more) {
        return;
    }

    GetClientRect(wd->hwnd, &rc);
    dcMem = CreateCompatibleDC(hdc);
    oldBmp = SelectObject(dcMem, g_resource->mor_bmp);
    BitBlt(hdc, rc.right - 72, rc.bottom - 18, 72, 18, dcMem, more_id * 72, 0, SRCINVERT);
    SelectObject(dcMem, oldBmp);
    DeleteDC(dcMem);
    more_id = more_id ? 0 : 1;
}

/***************************************************************************************
 * Check WINDESC validation
 ***************************************************************************************/
BOOL NHWnd_isValid(WINDESC *wd)
{
    WINDESC *chkwd = windowList;
    BOOL    result = FALSE;

    while (chkwd) {
        if (chkwd == wd) {
            result = TRUE;
            break;
        }
        chkwd = chkwd->next;
    }

    return result;
}

/***************************************************************************************
 *
 ***************************************************************************************/
void NHWnd_drawWall2(HDC hdc, RECT *rc, HBITMAP hbmp)
{
    BITMAP   bm;
    HDC      dcMem;
    HBITMAP  oldBmp;
    int      x, y;
    
    if (hbmp) {
        dcMem = CreateCompatibleDC(hdc);
        GetObject(hbmp, sizeof(BITMAP), &bm);
        oldBmp = SelectObject(dcMem, hbmp);
        for(y = rc->top; y < rc->bottom; y += min(bm.bmHeight, rc->bottom - y)) {
            for(x = rc->left; x < rc->right; x += min(bm.bmWidth, rc->right - x)) {
                BitBlt(hdc, x, y, min(bm.bmWidth, rc->right - x), 
                    min(bm.bmHeight, rc->bottom - y), dcMem, 0, 0, SRCCOPY);
            }
        }
        SelectObject(dcMem, oldBmp);
        DeleteDC(dcMem);
    } else {
        FillRect(hdc, rc, GetStockObject(BLACK_BRUSH));
    }
}

void NHWnd_drawWall(OFFSCREEN *ofb, RECT *rc, TILEDATA *tile)
{
    int x, y;

    if (tile != NULL) {
        for(y = rc->top; y < rc->bottom; y += min(tile->cy, rc->bottom - y)) {
            for(x = rc->left; x < rc->right; x += min(tile->cx, rc->right - x)) {
                Offscreen_blt(
                    ofb, 
                    x,
                    y,
                    min(tile->cx, rc->right - x), 
                    min(tile->cy, rc->bottom - y),
                    tile, 0, 0, BLTMODE_COPY);
            }
        }
    }
}

/***************************************************************************************
 * Draw text with shadow
 ***************************************************************************************/
void NHWnd_drawShadeText(HDC hdc, RECT *rc, char *buf, COLORREF fgColor, UINT format)
{
    int      oldMode;
    RECT     rcShadow;
    COLORREF oldColor;
    
    oldColor = SetTextColor(hdc, colormap[CLR_BLACK]);
    oldMode  = SetBkMode(hdc, TRANSPARENT);

    CopyRect(&rcShadow, rc);
    OffsetRect(&rcShadow, 2, 2);
    DrawText(hdc, buf, -1, &rcShadow, format);
    SetTextColor(hdc, fgColor);
    DrawText(hdc, buf, -1, rc, format);

    SetTextColor(hdc, oldColor);
    SetBkMode(hdc, oldMode);
}

/***************************************************************************************/
/* Recalculate all child windows' position and size
/***************************************************************************************/
void NHWnd_newLayout(void)
{
    WINDESC *wd = windowList;
    RECT    rcParent;
    RECT    rcMove;

    if (g_baseHwnd) {
        GetClientRect(g_baseHwnd, &rcParent);
        while (wd) {
            if (IsWindowVisible(wd->hwnd) && wd->procCalcRect) {
                wd->procCalcRect(wd, &rcParent, &rcMove);
                SetWindowPos(wd->hwnd, NULL,
                    rcMove.left, rcMove.top,
                    rcMove.right - rcMove.left,
                    rcMove.bottom - rcMove.top,
                    SWP_NOZORDER);
            }
            wd = wd->next;
        }

        /* To switch message and icon status window */
        /* I really hate this code */
        if (NHWnd_isValid((WINDESC *)WIN_MESSAGE) && MesgWnd_isBottom((WINDESC *)WIN_MESSAGE)) {
            RECT  rc;
            POINT pt1, pt2;
            wd = (WINDESC *)WIN_MESSAGE;
            GetWindowRect(wd->hwnd, &rc);
            pt1.x = rc.left; pt1.y = rc.top;
            ScreenToClient(g_baseHwnd, &pt1);
            pt2.x = rc.right; pt2.y = rc.bottom;
            ScreenToClient(g_baseHwnd, &pt2);
            rc.left = pt1.x; rc.top = pt1.y;
            rc.right = pt2.x; rc.bottom = pt2.y;
            StatWnd_moveIconWindow((WINDESC *)WIN_STATUS, &rc);
            SetWindowPos(wd->hwnd, NULL,
                rc.left, rc.top,
                rc.right - rc.left, rc.bottom - rc.top,
                SWP_NOZORDER|SWP_FRAMECHANGED);
       }
    }
}

/***************************************************************************************
 * More!
 ***************************************************************************************/
void NHWnd_more(WINDESC *wd)
{
    int id , ch;

    /* Window is required */
    if (wd && wd->hwnd) {
        
        wd->more = TRUE;
        /* Set timer for redraw */
        id = SetTimer(wd->hwnd, 1, 0, (TIMERPROC)MoreTimerProc);
        /* Wait for key input */
        do {
            ch = Key_getChar();
        } while (ch != VK_ESCAPE && ch != VK_RETURN && ch != VK_SPACE);
        /* Stop flushing */
        KillTimer(wd->hwnd, id);
        wd->more = FALSE;

        InvalidateRect(wd->hwnd, NULL, FALSE);
    }
}

/*-------------------------------------------------------------------------------------
 * More window timer callback function
 *-------------------------------------------------------------------------------------*/
static void CALLBACK MoreTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
{
    InvalidateRect(hwnd, NULL, FALSE);
    KillTimer(hwnd, idEvent);
    SetTimer(hwnd, idEvent, DEF_MOREINTERVAL, (TIMERPROC)MoreTimerProc);
}

/***************************************************************************************
 * Destroy window information structure
 ***************************************************************************************/
void NHWnd_destroyDesc(WINDESC *wd)
{
    WINDESC *tmpwd = windowList;

    /* If exist */
    if (NHWnd_isValid(wd)) {
        /* Remove window structure from link list */
        NHWnd_delDesc(wd);

        /* If window structure has Window, destroy it */
        if (wd->hwnd) {
            DestroyWindow(wd->hwnd);
        }
        if (wd->procDestroy) {
            wd->procDestroy(wd);
        }
        /* Free data */
        free(wd);
    }
}

/***************************************************************************************/
/* Recalculate all child windows' position and size
/***************************************************************************************/
void NHWnd_destroyAll()
{
    WINDESC *tmpwd = windowList;

    while (tmpwd) {
        NHWnd_destroyDesc(tmpwd);
        tmpwd = windowList;
    }
}

/***************************************************************************************/
/* Create window information structure
/***************************************************************************************/
WINDESC *NHWnd_createDesc(int type)
{
    WINDESC *newwin = NULL;
    BOOL    result = FALSE;

    /* Create new structure */
    newwin = (WINDESC*)calloc(1, sizeof(WINDESC));
    if (newwin) {
        switch (type) {
        case NHW_MAP:
            result = MapWnd_init(newwin, type);
            break;
        case NHW_STATUS:
            result = StatWnd_init(newwin, type);
            break;
        case NHW_MESSAGE:
            result = MesgWnd_init(newwin, type);
            break;
        case NHW_MENU:
        case NHW_TEXT:
            result = MenuWnd_init(newwin, type);
            break;
        default:
            break;
        }

        /* window specific data memory allocation error */
        if (!result) {
            free(newwin);
            newwin = NULL;
        } else {
            NHWnd_addDesc(newwin);
            if(newwin->create_at_init) {
                newwin->hwnd = CreateWindowEx(
                    newwin->exstyle,    /* window extrastyle   */
                    newwin->cname,      /* class name          */
                    newwin->wname,      /* window name         */
                    newwin->style,      /* window style        */
                    CW_USEDEFAULT,      /* horizontal position */
                    CW_USEDEFAULT,      /* vertical position   */
                    CW_USEDEFAULT,      /* width               */
                    CW_USEDEFAULT,      /* height              */
                    g_baseHwnd,         /* parent window       */
                    (HMENU)childId++,   /* child window id     */
                    g_hInstance,        /* instance handle     */
                    (LPVOID)newwin);    /* CREATESTRUCT        */
                ShowWindow(newwin->hwnd, SW_SHOW);
                NHWnd_newLayout();
            }
        }
    }

    return newwin;
}

/*-------------------------------------------------------------------------------------
 *
 *-------------------------------------------------------------------------------------*/
static void NHWnd_addDesc(WINDESC *wd)
{
    WINDESC *tmp;

    /* Add link list */
    if (windowList) {
        tmp = windowList;
        while(tmp->next) {
            tmp = tmp->next;
        }
        tmp->next = wd;
    } else {
        windowList = wd;
    }

    NHWnd_newLayout();
}

/*-------------------------------------------------------------------------------------
 *
 *-------------------------------------------------------------------------------------*/
static void NHWnd_delDesc(WINDESC *wd)
{
    WINDESC *tmpwd = windowList;

    if (wd == NULL) {
        return;
    } else if (wd == windowList) {
        windowList = windowList->next;
        return;
    }
    while (tmpwd != NULL) {
        if (wd == tmpwd->next) {
            tmpwd->next = wd->next;
            break;
        } else {
            tmpwd = tmpwd->next;
        }
    }
}

/***************************************************************************************/
/* WM_NCHITTEST
/***************************************************************************************/
UINT NHWnd_OnNCHitTest(HWND hwnd, int x, int y)
{
/*
    RECT rc;
    GetWindowRect(hwnd, &rc);
    InvalidateRect(hwnd, &rc, TRUE);
*/
    InvalidateRect(hwnd, NULL, FALSE);
    return DefWindowProc(hwnd, WM_NCHITTEST, 0, MAKELPARAM(x, y));
}


#endif /* NH2K_EXTENDS */
