/* 
CCIDE
Copyright 2001-2011 David Lindauer.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

You may contact the author at:
	mailto::camille@bluegrass.net
 */
#include <windows.h>
#include <commctrl.h>
#include <commdlg.h>
#include <richedit.h>
#include <stdio.h>
#include <ctype.h>

#include "header.h"
#include "operands.h"
#include "opcodes.h"

typedef struct
{
    TCData ptrs;
    int *olddataptr;
    int *currentdataptr;
    HTREEITEM hTreeItem;
    enum
    {
        floating = 1, editable = 2
    } flags;
} REGDATA;

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


extern HWND hwndSourceTab;
extern HINSTANCE hInstance;
extern HWND hwndClient, hwndStatus, hwndFrame;
extern PROCESS DebugProcess;
extern enum DebugState uState;
extern HWND hwndTab;
extern THREAD *StoppedThread;

HWND hwndRegister;
static CONTEXT regContext;
static HWND hwndTree;
static char szRegisterClassName[] = "xccRegisterClass";
static char szRegisterTitle[] = "";
static LOGFONT fontdata = 
{
    16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH |
        FF_DONTCARE, "Courier New"
};
static HWND hwndCtrl;
static HANDLE registerHandle;
static CONTEXT oldContext;
static HBITMAP valueBitmap, itemBitmap;
static char *szRegTitle = "Register Window";

static char xeax[] = "EAX";
static char xebx[] = "EBX";
static char xecx[] = "ECX";
static char xedx[] = "EDX";
static char xesi[] = "ESI";
static char xedi[] = "EDI";
static char xesp[] = "ESP";
static char xebp[] = "EBP";
static char xeip[] = "EIP";
static char xst0[] = "ST0";
static char xst1[] = "ST1";
static char xst2[] = "ST2";
static char xst3[] = "ST3";
static char xst4[] = "ST4";
static char xst5[] = "ST5";
static char xst6[] = "ST6";
static char xst7[] = "ST7";
static char xeflags[] = "EFLAGS";
static char eaxbuf[20];
static char ebxbuf[20];
static char ecxbuf[20];
static char edxbuf[20];
static char esibuf[20];
static char edibuf[20];
static char espbuf[20];
static char ebpbuf[20];
static char eipbuf[20];
static char eflagsbuf[20];
static char st0buf[40];
static char st1buf[40];
static char st2buf[40];
static char st3buf[40];
static char st4buf[40];
static char st5buf[40];
static char st6buf[40];
static char st7buf[40];

static char regst[] = "Arithmetic";
static char control[] = "Control";
static char floatingname[] = "Floating";
static REGDATA regs[] = 
{
    {
        {
             &regst, 0
        }
        , 0, 0, 0, 
    }
    , 
    {
        {
             &xeax, &eaxbuf
        }
        , &oldContext.Eax, &regContext.Eax, 0, editable
    }
    , 
    {
        {
             &xebx, &ebxbuf
        }
        , &oldContext.Ebx, &regContext.Ebx, 0, editable
    }
    , 
    {
        {
             &xecx, &ecxbuf
        }
        , &oldContext.Ecx, &regContext.Ecx, 0, editable
    }
    , 
    {
        {
             &xedx, &edxbuf
        }
        , &oldContext.Edx, &regContext.Edx, 0, editable
    }
    , 
    {
        {
             &xesi, &esibuf
        }
        , &oldContext.Esi, &regContext.Esi, 0, editable
    }
    , 
    {
        {
             &xedi, &edibuf
        }
        , &oldContext.Edi, &regContext.Edi, 0, editable
    }
    , 
    {
        {
             &control, 0
        }
        , 0, 0, 0, 
    }
    , 
    {
        {
             &xesp, &espbuf
        }
        , &oldContext.Esp, &regContext.Esp, 0, editable
    }
    , 
    {
        {
             &xebp, &ebpbuf
        }
        , &oldContext.Ebp, &regContext.Ebp, 0, editable
    }
    , 
    {
        {
             &xeip, &eipbuf
        }
        , &oldContext.Eip, &regContext.Eip, 0, editable
    }
    , 
    {
        {
             &xeflags, &eflagsbuf
        }
        , &oldContext.EFlags, &regContext.EFlags, 0, editable
    }
    , 
    {
        {
             &floatingname, 0
        }
        , 0, 0, 0, 
    }
    , 
    {
        {
             &xst0, &st0buf
        }
        , &oldContext.FloatSave.RegisterArea[0],
            &regContext.FloatSave.RegisterArea[0], 0, editable | floating
    }
    , 
    {
        {
             &xst1, &st1buf
        }
        , &oldContext.FloatSave.RegisterArea[10],
            &regContext.FloatSave.RegisterArea[10], 0, editable | floating
    }
    , 
    {
        {
             &xst2, &st2buf
        }
        , &oldContext.FloatSave.RegisterArea[20],
            &regContext.FloatSave.RegisterArea[20], 0, editable | floating
    }
    , 
    {
        {
             &xst3, &st3buf
        }
        , &oldContext.FloatSave.RegisterArea[30],
            &regContext.FloatSave.RegisterArea[30], 0, editable | floating
    }
    , 
    {
        {
             &xst4, &st4buf
        }
        , &oldContext.FloatSave.RegisterArea[40],
            &regContext.FloatSave.RegisterArea[40], 0, editable | floating
    }
    , 
    {
        {
             &xst5, &st5buf
        }
        , &oldContext.FloatSave.RegisterArea[50],
            &regContext.FloatSave.RegisterArea[50], 0, editable | floating
    }
    , 
    {
        {
             &xst6, &st6buf
        }
        , &oldContext.FloatSave.RegisterArea[60],
            &regContext.FloatSave.RegisterArea[60], 0, editable | floating
    }
    , 
    {
        {
             &xst7, &st7buf
        }
        , &oldContext.FloatSave.RegisterArea[70],
            &regContext.FloatSave.RegisterArea[70], 0, editable | floating
    }
    , 
};

static TCHeader tch = 
{
    "Register", "Value", 0, 0
};
static int drawn;
void SaveRegisterContext(void)
{
    oldContext = regContext;
    StoppedThread->regs = regContext;
}

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

LRESULT CALLBACK _export RegisterProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    static int charwidth;
    HDC dc;
    TEXTMETRIC metric;
    DEBUG_EVENT *xc;
    HFONT oldFont;
    LOGBRUSH brushstr;
    RECT r;
    int i;
    NM_TREEVIEW *nmt;
    switch (iMessage)
    {
        case WM_MDIACTIVATE:
            SendMessage(hwndCtrl, LCF_SETACTIVE, 0, lParam == (LPARAM)hwnd);
            if (lParam == (LPARAM)hwnd)
            {
                SendMessage(hwndSourceTab, WM_SETACTIVETAB, 0, (LPARAM)hwnd);
                SetFocus(hwndTree);
            }
            return TRUE;
        case WM_NOTIFY:
            nmt = (NM_TREEVIEW*)lParam;
            switch (nmt->hdr.code)
            {
            case TCN_EDITQUERY:
                if (uState != atBreakpoint)
                    return 0;
                for (i = 0; i < sizeof(regs) / sizeof(REGDATA); i++)
                    if (regs[i].hTreeItem == nmt->itemNew.hItem)
                        if (regs[i].flags &editable)
                            return (LRESULT)regs[i].ptrs.col2Text;
                return 0;
            case TCN_EDITDONE:
                for (i = 0; i < sizeof(regs) / sizeof(REGDATA); i++)
                if (regs[i].hTreeItem == nmt->itemNew.hItem)
                {
                    char *buf = (char*)nmt->itemNew.pszText;
                    if (regs[i].flags &floating)
                    {
                        float val;
                        int pos = regs[i].currentdataptr;
                        sscanf(buf, "%f", &val);
                        *(long double*)pos = val;
                        sprintf(regs[i].ptrs.col2Text, "%f", (double)val);
                    }
                    else
                    {
                        if (buf[0] == '0' && buf[1] == 'x')
                            sscanf(buf + 2, "%x", regs[i].currentdataptr);
                        else if ((buf[strlen(buf) - 1] &0xDF) == 'H')
                            sscanf(buf, "%x", regs[i].currentdataptr);
                        else
                            sscanf(buf, "%d", regs[i].currentdataptr);
                        sprintf(regs[i].ptrs.col2Text, "0x%08x", 
                            *regs[i].currentdataptr);
                    }
                    regs[i].ptrs.col2Color = 0xff;
                }
                return 0;
            }
            break;
        case WM_SYSCOMMAND:
            if (wParam == SC_CLOSE)
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            break;
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case ID_SETCONTEXT:
                SaveRegisterContext();
                break;
            case ID_SETADDRESS:
                regContext = StoppedThread->regs;
                registerHandle = (HANDLE)lParam;
                if (lParam)
                {
                    int i;
                    for (i = 0; i < sizeof(regs) / sizeof(REGDATA); i++)
                    if (regs[i].currentdataptr)
                    {
                        if (regs[i].flags &floating)
                        {
                            double val;
                            int pos = regs[i].currentdataptr;
                            val = *(long double*)pos;

                            sprintf(regs[i].ptrs.col2Text, "%f", val);
                            if (!memcmp(regs[i].currentdataptr,
                                regs[i].olddataptr, 10))
                                regs[i].ptrs.col2Color = 0;
                            else
                                regs[i].ptrs.col2Color = 0xff;
                        }
                        else
                        {
                            sprintf(regs[i].ptrs.col2Text, "0x%08x", 
                                *regs[i].currentdataptr);
                            if (*regs[i].currentdataptr ==  *regs[i].olddataptr)
                                regs[i].ptrs.col2Color = 0;
                            else
                                regs[i].ptrs.col2Color = 0xff;
                        }
                    }
                    if (!drawn)
                    {
                        HTREEITEM after1 = 0, after2 = 0;
                        for (i = 0; i < sizeof(regs) / sizeof(REGDATA); i++)
                        {
                            TV_INSERTSTRUCT t;
                            memset(&t, 0, sizeof(t));
                            t.hParent = regs[i].currentdataptr ? after1 : 0;
                            t.hInsertAfter = regs[i].currentdataptr ? after2 :
                                after1;
                            #if !defined( _WIN32_IE) && !defined(__CCDL__)
                                t.item.mask = 0;
                                t.item.hItem = 0;
                                t.item.lParam = (int) &regs[i].ptrs;
                            #else 
                                t.u.item.mask = 0;
                                t.u.item.hItem = 0;
                                t.u.item.lParam = (int) &regs[i].ptrs;
                            #endif 
                            regs[i].hTreeItem = TreeView_InsertItem(hwndTree,
                                &t);
                            if (!regs[i].currentdataptr)
                            {
								TreeView_Expand(hwndTree, after1, TVE_EXPAND);
                                after1 = regs[i].hTreeItem;
                                after2 = 0;
                            }
                            else
                                after2 = regs[i].hTreeItem;
							
                        }
						TreeView_Expand(hwndTree, after1, TVE_EXPAND);
                        drawn = TRUE;
                    }
                }
                else if (!lParam && drawn)
                {
                    TreeView_DeleteAllItems(hwndTree);
                    drawn = FALSE;
                }
                InvalidateRect(hwndTree, 0, 0);
                break;
            default:
                return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
            }
            break;
        case WM_SETFOCUS:
            break;
        case WM_KILLFOCUS:
            break;
        case WM_LBUTTONDOWN:
            break;
        case WM_CREATE:
            hwndRegister = hwnd;
            GetClientRect(hwnd, &r);
            hwndCtrl = CreateControlWindow(DID_REGWND, hwnd, &r, (int)(
                (LPMDICREATESTRUCT)(*(int*)lParam))->lParam, szRegTitle);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            //         valueBitmap = LoadBitmap(hInstance, "ID_VALUEBMP") ;
            //         itemBitmap = LoadBitmap(hInstance, "ID_ITEMBMP") ;
            tch.colBmp1 = itemBitmap;
            tch.colBmp2 = valueBitmap;
            hwndTree = CreateextTreeWindow(hwndCtrl, TCS_LINE, &r, &tch);
            break;

        case WM_CLOSE:
            dmgrHideWindow(DID_REGWND, TRUE);
            return 0;
        case WM_DESTROY:
            DestroyWindow(hwndTree);
            //         DeleteObject(itemBitmap) ;
            //         DeleteObject(valueBitmap) ;
            hwndRegister = 0;
            registerHandle = 0;
            break;
        case WM_SIZE:
            r.left = r.top = 0;
            r.right = LOWORD(lParam);
            r.bottom = HIWORD(lParam);
            MoveWindow(hwndCtrl, 0, 0, r.right, r.bottom, TRUE);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            MoveWindow(hwndTree, r.left, r.top, r.right - r.left, r.bottom -
                r.top, TRUE);
            break;
        case WM_INITMENUPOPUP:
            break;
        default:
            break;
    }
    return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
}

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

void RegisterRegisterWindow(void)
{
    WNDCLASS wc;
    memset(&wc, 0, sizeof(wc));
    wc.style = 0;
    wc.lpfnWndProc = &RegisterProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(0, IDI_APPLICATION);
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = 0;
    wc.lpszClassName = szRegisterClassName;
    RegisterClass(&wc);
}

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

HWND CreateRegisterWindow(void)
{
    MDICREATESTRUCT mc;
    HWND rv;
    RECT r;
    if (hwndRegister)
    {
        SendMessage(hwndRegister, WM_SETFOCUS, 0, 0);
        return hwndRegister;
    }
    GetClientRect(hwndClient, &r);
    mc.szClass = szRegisterClassName;
    mc.szTitle = szRegTitle;
    mc.hOwner = hInstance;
    mc.x = CW_USEDEFAULT;
    mc.y = CW_USEDEFAULT;
    mc.cx = 200;
    mc.cy = 400;
    mc.style = WS_CHILD | WS_CLIPSIBLINGS | WS_DLGFRAME;
    mc.lParam = (LPARAM)0;
    rv = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM) &mc);
    return rv;
}
