/* 
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 HINSTANCE hInstance;
extern HWND hwndClient, hwndStatus, hwndFrame;
extern PROCESS DebugProcess;
extern enum DebugState uState;
extern THREAD *StoppedThread;

typedef struct _stacklist
{
    struct _stacklist *next;
    int address;
    char name[256];
} STACKLIST;

HWND hwndStack;

static char szStackClassName[] = "xccStackClass";
static char szStackBlankClassName[] = "xccStackClass2";
static HWND hwndCtrl, hwndBlank;

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 StackFont;
static int StackAddress, StackLines;
static STACKLIST *StackList;
static int curpos;
static char *szStackTitle = "Call Stack Window";
void StackDoPaint(HWND hwnd)
{
    int i;
    STACKLIST *list = StackList;
    int Address = StackAddress;
    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);
    oldFont = SelectObject(dc, StackFont);
    while (list && Address)
    {
        list = list->next;
        Address -= 1;
    }
    for (i = 0; i < lines && list; i++)
    {
        sprintf(buf, "%08X: %s", list->address, list->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);
}

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

void ClearStackArea(HWND hwnd)
{
    while (StackList)
    {
        STACKLIST *s = StackList->next;
        free(StackList);
        StackList = s;
    }
    StackLines = 0;
    StackAddress = 0;
    curpos = 0;
    SetScrollRange(hwnd, SB_VERT, StackAddress, StackLines, FALSE);
    SetScrollPos(hwnd, SB_VERT, 0, TRUE);
}

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

int eipReal(int eip)
{
    int len, i;
    unsigned char buf[16];
    ReadProcessMemory(DebugProcess.hProcess, (LPVOID)(eip - 16), (LPVOID) &buf,
        16, &len);
    if (buf[11] == 0xe8)
        return eip - 5;
    for (i = 14; i >= 0; i--)
        if (buf[i] == 0xff && ((buf[i] &0x38) == 0x10))
            return eip - (16-i);
    return eip;
}

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

int readStackedData(int inebp, int *outebp)
{
    DWORD eip = 0;
    int len = 0;
    ReadProcessMemory(DebugProcess.hProcess, (LPVOID)(inebp + 4), (LPVOID) &eip,
        4, &len);
    ReadProcessMemory(DebugProcess.hProcess, (LPVOID)inebp, (LPVOID)outebp, 4,
        &len);
    return eip;
}

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

int findStackedFunction(int eipCursor, int *outebp, int *level, THREAD **thread)
{
    PROCSYM32 *cursym = FindFunctionSymbol(eipCursor);
    THREAD *l = DebugProcess.threads;
    while (l)
    {
        int eip;
        eip = l->regs.Eip;
        *outebp = l->regs.Ebp;
        *level = 0;
        *thread = l;
        while (eip)
        {
            PROCSYM32 *tempsym = FindFunctionSymbol(eip);
            if (tempsym == cursym)
                return eip;
            eip = readStackedData(*outebp, outebp);
            (*level)++;
        }
        l = l->next;
    }
    return 0;
}

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

void SetStackArea(HWND hwnd)
{
    int ebp = StoppedThread->regs.Ebp;
    int eip = StoppedThread->regs.Eip;
    int len;
    STACKLIST *stackbase = 0,  *stackptr,  *newstack;
    char *symtab;
    int offset;
    char buf[256];
    int lines = 0;

    StackLines = 0;
    if (uState != atBreakpoint && uState != atException)
        return ;
    while (1)
    {
        eip = eipReal(eip);
        FindFunctionName(buf, eip);
        newstack = calloc(1,sizeof(STACKLIST));
        if (!newstack)
            return ;
        newstack->next = 0;
        strcpy(newstack->name, buf);
        newstack->address = eip;
        if (stackbase)
            stackptr = stackptr->next = newstack;
        else
            stackbase = stackptr = newstack;
        eip = 0;
        lines++;
        eip = readStackedData(ebp, &ebp);
        if (!eip)
            break;
    }
    StackList = stackbase;
    StackLines = lines;
    SetScrollRange(hwnd, SB_VERT, 0, lines - 1, TRUE);
}

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

LRESULT CALLBACK _export StackBlankProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    RECT r;
    char module[256];
    STACKLIST *sl;
    int i, lines;
    switch (iMessage)
    {
        case WM_PAINT:
            StackDoPaint(hwnd);
            return 0;
        case WM_SETFOCUS:
            //			SendMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0) ;
            break;
        case WM_KILLFOCUS:
            break;
        case WM_LBUTTONDOWN:
            lines = HIWORD(lParam);
            if (lines / 16+StackAddress < StackLines)
            {
                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 = StackList;
                lines = StackAddress + curpos;
                while (sl && lines)
                {
                    sl = sl->next;
                    lines--;
                }
                if (sl)
                {
                    if (GetBreakpointLine(sl->address, 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.logMRU = FALSE;
						x.newFile = FALSE;
                        CreateDrawWindow(&x, TRUE);
                    }
                }
                break;
            case VK_UP:
                if (curpos)
                    curpos--;
                else if (StackAddress)
                {
                    StackAddress--;
                    SetScrollPos(hwnd, SB_VERT, StackAddress, TRUE);
                }
                InvalidateRect(hwnd, 0, 0);
                break;
            case VK_DOWN:
                GetClientRect(hwnd, &r);
                lines = r.bottom / 16;
                if (curpos < StackLines - 1)
                {
                    if (curpos < lines - 1)
                        curpos++;
                    else if (StackAddress < StackLines - 1)
                    {
                        StackAddress++;
                        SetScrollPos(hwnd, SB_VERT, StackAddress, TRUE);
                    }
                    InvalidateRect(hwnd, 0, TRUE);
                }
                break;
            }
            break;
        case WM_VSCROLL:
            GetClientRect(hwnd, &r);
            lines = r.bottom / 16;
            switch (LOWORD(wParam))
            {
            case SB_BOTTOM:
                StackAddress = StackLines - (lines *1);
                break;
            case SB_TOP:
                StackAddress = 0;
                break;
            case SB_ENDSCROLL:
                return 0;
            case SB_LINEDOWN:
                StackAddress += 1;
                break;
            case SB_LINEUP:
                StackAddress -= 1;
                break;
            case SB_PAGEDOWN:
                StackAddress += 1 *(lines - 1);
                break;
            case SB_PAGEUP:
                StackAddress -= 1 *(lines - 1);
                break;
            case SB_THUMBPOSITION:
            case SB_THUMBTRACK:
                StackAddress = HIWORD(wParam);
                break;
            }
            if (StackAddress < 0)
                StackAddress = 0;
            if (StackAddress >= StackLines)
                StackAddress = StackLines - 1;
            InvalidateRect(hwnd, 0, 0);
            SetScrollPos(hwnd, SB_VERT, StackAddress, TRUE);
            return 0;
    }
    return DefWindowProc(hwnd, iMessage, wParam, lParam);
}

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

LRESULT CALLBACK _export StackProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    int i, lines;
    HDC dc;
    PAINTSTRUCT ps;
    TEXTMETRIC metric;
    DEBUG_EVENT *xc;
    HFONT oldFont;
    LOGBRUSH brushstr;
    RECT r;
    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_CREATE:
            hwndStack = hwnd;
            GetClientRect(hwnd, &r);
            hwndCtrl = CreateControlWindow(DID_STACKWND, hwnd, &r, (int)(
                (LPMDICREATESTRUCT)(*(int*)lParam))->lParam, szStackTitle);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            hwndBlank = CreateWindow(szStackBlankClassName, 0, WS_VSCROLL +
                WS_CHILD + WS_CLIPSIBLINGS + WS_BORDER + WS_VISIBLE, r.left,
                r.top, r.right - r.left, r.bottom - r.top, hwndCtrl, 0,
                hInstance, 0);
            StackFont = CreateFontIndirect(&fontdata);
            wParam = 1;
            // fall through
        case WM_RESTACK:
            ClearStackArea(hwndBlank);
            if (uState != notDebugging && wParam)
                SetStackArea(hwndBlank);
            InvalidateRect(hwndBlank, 0, 0);
            break;
        case WM_PAINT:
            dc = BeginPaint(hwnd, &ps);
            EndPaint(hwnd, &ps);
            return 0;
        case WM_CLOSE:
            dmgrHideWindow(DID_STACKWND, TRUE);
            return 0;
        case WM_DESTROY:
            dmgrRemoveClient((CCW_params*)GetWindowLong(hwndCtrl, 0));
            DestroyWindow(hwndBlank);
            ClearStackArea(hwnd);
            hwndStack = 0;
            DeleteObject(StackFont);
            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(hwndBlank, r.left, r.top, r.right - r.left, r.bottom -
                r.top, TRUE);
            break;
        case WM_INITMENUPOPUP:
            return 0;
        default:
            break;
    }
    return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
}

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

void RegisterStackWindow(void)
{
    WNDCLASS wc;
    memset(&wc, 0, sizeof(wc));
    wc.style = CS_HREDRAW + CS_VREDRAW + CS_DBLCLKS;
    wc.lpfnWndProc = &StackProc;
    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 = szStackClassName;
    RegisterClass(&wc);
    wc.lpfnWndProc = &StackBlankProc;
    wc.lpszClassName = szStackBlankClassName;
    RegisterClass(&wc);
}

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

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