/* 
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 "header.h"
#include "operands.h"
#include "opcodes.h"
#include <ctype.h>
#include "cvinfo.h"

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

HWND hwndThread;

static char szThreadClassName[] = "xccThreadClass";
static char szThreadBlankClassName[] = "xccThreadClass2";
static HWND hwndCtrl, hwndBlank;
static int DisplayThreads;

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 HFONT ThreadFont;
static int ThreadAddress, ThreadLines;
static int curpos;
static char *szThreadTitle = "Thread Window";

void ThreadDoPaint(HWND hwnd)
{
    int i;
    THREAD *list = DebugProcess.threads;
    int Address = ThreadAddress;
    char buf[256],  *p;
    HBRUSH wbrush = GetStockObject(WHITE_BRUSH);
    PAINTSTRUCT ps;
    CONTEXT context;
    HDC dc;
    HFONT oldFont;
    RECT rect;
    int lines;
    GetClientRect(hwnd, &rect);
    lines = (rect.bottom - rect.top) / 16;
    dc = BeginPaint(hwnd, &ps);
    FillRect(dc, &rect, wbrush);
    SelectObject(dc, ThreadFont);
    while (list && Address)
    {
        list = list->next;
        Address -= 1;
    }
    if (DisplayThreads)
    {
        for (i = 0; i < lines && list; i++)
        {
            char name[200];
            int eip = list->regs.Eip;
            if (!FindFunctionName(name, eip))
                name[0] = 0;
            if (list->idThread == StoppedThread->idThread)
                strcat(name, "*");
            sprintf(buf, "%08x %08X: %s", list->idThread, eip, name);

            if (i == curpos)
            {
                int oldtext = GetTextColor(dc);
                int oldbk = GetBkColor(dc);
                SetTextColor(dc, oldbk);
                SetBkColor(dc, oldtext);
                TextOut(dc, 0, i *16+rect.top, buf, strlen(buf));
                SetTextColor(dc, oldtext);
                SetBkColor(dc, oldbk);
            }
            else
                TextOut(dc, 0, i *16+rect.top, buf, strlen(buf));
            list = list->next;
             \
            }
    }

    SelectObject(dc, oldFont);

    EndPaint(hwnd, &ps);
}

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

LRESULT CALLBACK _export ThreadBlankProc(HWND hwnd, UINT iMessage, WPARAM
    wParam, LPARAM lParam)
{
    int i, lines;
    RECT r;
    char module[256];
    THREAD *sl;
    switch (iMessage)
    {
        case WM_PAINT:
            ThreadDoPaint(hwnd);
            return 0;
        case WM_RESTACK:
            if (DisplayThreads)
            {
                int i = 0;
                THREAD *list = DebugProcess.threads;
                while (list)
                    i++, list = list->next;
                SetScrollRange(hwnd, SB_VERT, 0, i - 1, TRUE);
                ThreadLines = i;
                ThreadAddress = 0;
            }
            else
                SetScrollRange(hwnd, SB_VERT, 0, 1, TRUE);
            break;
        case WM_SETFOCUS:
            SendMessage(hwndFrame, WM_REDRAWTOOLBAR, 0, 0);
            break;
        case WM_KILLFOCUS:
            break;
        case WM_LBUTTONDOWN:
            lines = HIWORD(lParam);
            if (lines / 16+ThreadAddress < ThreadLines)
            {
                curpos = lines / 16;
                InvalidateRect(hwnd, 0, TRUE);
            }
            // fall through
        case WM_RBUTTONDOWN:
            SetFocus(hwnd);
            break;
        case WM_LBUTTONDBLCLK:
            PostMessage(hwnd, WM_KEYDOWN, VK_RETURN, 0);
            break;
        case WM_KEYDOWN:
            switch (wParam)
            {
            case VK_RETURN:
                sl = DebugProcess.threads;
                lines = ThreadAddress + curpos;
                while (sl && lines)
                {
                    sl = sl->next;
                    lines--;
                }
                if (sl)
                {
                    int eip = sl->regs.Eip;
                    if (GetBreakpointLine(eip, module, &lines))
                    {
                        char *p;
                        char nmodule[256];
                        static DWINFO x;
                        FindModuleName(nmodule, module);
                        strcpy(x.dwName, nmodule);
                        p = strrchr(nmodule, '\\');
                        if (p)
                            strcpy(x.dwTitle, p + 1);
                        else
                            strcpy(x.dwTitle, nmodule);
                        x.dwLineNo = lines;
						x.newFile = FALSE;
                        CreateDrawWindow(&x,TRUE);
                    }
                }
                break;
            case VK_UP:
                if (curpos)
                    curpos--;
                else if (ThreadAddress)
                    ThreadAddress--;
                InvalidateRect(hwnd, 0, 0);
                break;
            case VK_DOWN:
                GetClientRect(hwnd, &r);
                lines = r.bottom / 16;
                if (curpos < ThreadLines - 1)
                {
                    if (curpos < lines - 1)
                        curpos++;
                    else if (ThreadAddress < ThreadLines - 1)
                        ThreadAddress++;
                    InvalidateRect(hwnd, 0, TRUE);
                }
                break;
            }
            break;
        case WM_VSCROLL:
            GetClientRect(hwnd, &r);
            lines = r.bottom / 16;
            switch (LOWORD(wParam))
            {
            case SB_BOTTOM:
                ThreadAddress = ThreadLines - (lines *1);
                break;
            case SB_TOP:
                ThreadAddress = 0;
                break;
            case SB_ENDSCROLL:
                return 0;
            case SB_LINEDOWN:
                ThreadAddress += 1;
                break;
            case SB_LINEUP:
                ThreadAddress -= 1;
                break;
            case SB_PAGEDOWN:
                ThreadAddress += 1 *(lines - 1);
                break;
            case SB_PAGEUP:
                ThreadAddress -= 1 *(lines - 1);
                break;
            case SB_THUMBPOSITION:
            case SB_THUMBTRACK:
                ThreadAddress = HIWORD(wParam);
                break;
            }
            if (ThreadAddress < 0)
                ThreadAddress = 0;
            if (ThreadAddress >= ThreadLines)
                ThreadAddress = ThreadLines - 1;
            InvalidateRect(hwnd, 0, 0);
            SetScrollPos(hwnd, SB_VERT, ThreadAddress, TRUE);
            return 0;
    }
    return DefWindowProc(hwnd, iMessage, wParam, lParam);
}

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

LRESULT CALLBACK _export ThreadProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    int i, lines;
    HDC dc;
    TEXTMETRIC metric;
    DEBUG_EVENT *xc;
    HFONT oldFont;
    LOGBRUSH brushstr;
    RECT r;
    char module[256];
    THREAD *sl;
    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(hwndBlank);
            }
            return TRUE;
        case WM_SETFOCUS:
            SetFocus(hwndBlank);
            break;
        case WM_SYSCOMMAND:
            if (wParam == SC_CLOSE)
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            break;
        case WM_COMMAND:
            break;
        case WM_RESTACK:
            DisplayThreads = uState != notDebugging && wParam;
            SendMessage(hwndBlank, WM_RESTACK, wParam, lParam);
            break;
        case WM_CREATE:
            hwndThread = hwnd;
            GetClientRect(hwnd, &r);
            hwndCtrl = CreateControlWindow(DID_THREADWND, hwnd, &r, (int)(
                (LPMDICREATESTRUCT)(*(int*)lParam))->lParam, szThreadTitle);
            ThreadFont = CreateFontIndirect(&fontdata);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            hwndBlank = CreateWindow(szThreadBlankClassName, 0, WS_CHILD +
                WS_VSCROLL + WS_CLIPSIBLINGS + WS_BORDER + WS_VISIBLE, r.left,
                r.top, r.right - r.left, r.bottom - r.top, hwndCtrl, 0,
                hInstance, 0);

            break;
        case WM_CLOSE:
            dmgrHideWindow(DID_THREADWND, TRUE);
            return 0;
        case WM_DESTROY:
            dmgrRemoveClient((CCW_params*)GetWindowLong(hwndCtrl, 0));
            DestroyWindow(hwndBlank);
            hwndThread = 0;
            DeleteObject(ThreadFont);
            break;
        case WM_INITMENUPOPUP:
            return 0;
        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(hwndBlank, r.left, r.top, r.right - r.left, r.bottom -
                r.top, TRUE);
            break;
        default:
            break;
    }
    return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
}

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

void RegisterThreadWindow(void)
{
    WNDCLASS wc;
    memset(&wc, 0, sizeof(wc));
    wc.style = CS_HREDRAW + CS_VREDRAW + CS_DBLCLKS;
    wc.lpfnWndProc = &ThreadProc;
    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 = szThreadClassName;
    RegisterClass(&wc);
    wc.lpfnWndProc = &ThreadBlankProc;
    wc.lpszClassName = szThreadBlankClassName;
    RegisterClass(&wc);
}

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

HWND CreateThreadWindow(void)
{
    CCW_params p;
    MDICREATESTRUCT mc;
    HWND rv;
    RECT r;
    if (hwndThread)
    {
        SendMessage(hwndThread, WM_SETFOCUS, 0, 0);
        return hwndThread;
    }
    GetClientRect(hwndClient, &r);
    mc.szClass = szThreadClassName;
    mc.szTitle = szThreadTitle;
    mc.hOwner = hInstance;
    mc.x = CW_USEDEFAULT;
    mc.y = CW_USEDEFAULT;
    mc.cx = 30 * 8;
    mc.cy = 19 * 8;
    mc.style = WS_CLIPCHILDREN | WS_CHILD | WS_CLIPSIBLINGS | WS_DLGFRAME,
        mc.lParam = (LPARAM)0;
    rv = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM) &mc);
    return rv;
}
