/*
 * w_common.c : common routines for NetHack windows
 *
 * Copyright (c) Yukihiko Aoki 1999
 * 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);
    GetWindowRect(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,
        rc_parent.left + pt_indent.x,
        rc_parent.top + 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 TRUE;
}

/***************************************************************************************
 * 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, SRCCOPY);
    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_drawWall(HDC hdc, RECT *rc)
{
    BITMAP   bm;
    HDC      dcMem;
    HBITMAP  oldBmp;
    HPALETTE oldPal;
    int      x, y;
    
    if(g_resource->wal_bmp) {
        dcMem = CreateCompatibleDC(hdc);
        GetObject(g_resource->wal_bmp, sizeof(BITMAP), &bm);
        oldBmp = SelectObject(dcMem, g_resource->wal_bmp);
        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));
    }
}

/***************************************************************************************
 * 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(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(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 */
        DELMEMORY((HGLOBAL)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*)NEWMEMORY(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) {
            DELMEMORY(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        */
            }
        }
    }

    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)
{
    InvalidateRect(hwnd, NULL, FALSE);
    return DefWindowProc(hwnd, WM_NCHITTEST, 0, MAKELPARAM(x, y));
}


#endif /* NH2K_EXTENDS */
