/* 
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>

#define ASM_OFFSET 35

#define LINES 512
#define LINELEN 564

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

HWND hwndASM;
HWND hwndASMBlank;
HBRUSH pcBrush, stopBrush;
static HBITMAP pcBitmap, stopBitmap;
static HWND hwndCtrl;

static char **lines;
static int *addrs;
static int curline, shownLine;

static char szASMClassName[] = "xccASMClass";
static char szASMBlankClassName[] = "xccASMClass2";
static char *szASMTitle = "Disassembly Window";
static int asmAddress = 0x410000, asmIP;
static DWORD threadID = 0;
static int rePosition = 0;
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 asmFont;
static int caretX, caretY;
static int charwidth;

extern long code_address;
BYTE *code_ptr;
BOOL GetCodeLine(char *s)
{
    BOOL isNewLine = TRUE;
    BYTE *oldposition = code_ptr;
    char *put;
    OPTABLE *theTable;
    s[0] = 0;
    sprintf(s, "%08x: ", (int)(code_address));
    put = TabTo(s, 11);
    code_ptr = ReadOverrides(code_ptr, VAL_386 | VAL_FLOAT);
    if (!(theTable = FindOpcode(code_ptr, VAL_386 | VAL_FLOAT)))
    {
        strcpy(put, "db");
        put = TabTo(put, TAB_ARGPOS);
        sprintf(put, "%02x",  *code_ptr++);
    }
    else
    {
        code_ptr = DispatchOperand(code_ptr, theTable, TRUE);
        FormatDissassembly(put);
    }
    code_address += code_ptr - oldposition;
}

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

char *getline(char *module, int lineno)
{
    static char line[256];
    static char oldmodule[256];
    static int oldline;
    static FILE *infile;
    char buffer[1024],  *rline;
    int l;

    line[0] = 0;
    if (infile && (lineno == 0 || strcmp(module, oldmodule) || lineno <=
        oldline))
    {
        fclose(infile);
        infile = 0;
        oldline = 0;
    }
    strcpy(oldmodule, module);
    if (lineno == 0)
        return lineno;
    if (!infile)
        infile = fopen(module, "r");
    if (!infile)
        return line;

    do
    {
        buffer[0] = 0;
        fgets(buffer, 1024, infile);
    }
    while (!feof(infile) && ++oldline < lineno);
    if (!feof(infile))
        strncpy(line, buffer, 255);
    rline = line;
    while (isspace(*rline))
        rline++;
    l = strlen(rline) - 1;
    if (l >= 0 && rline[l] < 32)
        rline[l] = 0;
    return rline;
}

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

void CalculateDisassembly(int moving)
{
    RECT r;
    int rv = 0;
    int lineno;
    char buffer[256];
    char buf1[256];
    BYTE dbuf[32];
    int i;
    int linecount;
    GetClientRect(hwndASMBlank, &r);
    linecount = (r.bottom - r.top) / 16;
    code_ptr = dbuf;

    i = 0;
    code_address = asmAddress - LINES / 2;
    while (code_address < asmAddress + LINES / 2 && i < LINES)
    {
        code_ptr = dbuf;
        if (code_address >= asmAddress && !rv)
            rv = i;
        if (!ReadProcessMemory(DebugProcess.hProcess, (LPVOID)(code_address),
            dbuf, 32, 0))
        {
            addrs[i] = code_address;
            sprintf(lines[i++], "%08X: ??", code_address++);
        }
        else
        {
            DEBUG_INFO *dbg;
            char *types,  *syms;
            int offset;
            if (GetBreakpointLine(code_address, buf1, &lineno) == code_address)
            {
                addrs[i] = code_address;
                sprintf(lines[i++], "MODULE: %s LINE: %d %s", relpath(buf1, debugProject->projectName), lineno,
                    getline(buf1, lineno));
            }
            if (FindSymbolByAddress(&dbg, &types, &syms, &offset, code_address,
                buffer))
            {
                addrs[i] = code_address;
                strcat(buffer, ":");
                strcpy(lines[i++], buffer);
            }
            addrs[i] = code_address;
            GetCodeLine(lines[i++]);
        }
    }
    if (!moving)
    {
        shownLine = rv - linecount / 2;
    }
    else
    {
        shownLine = rv;
        if (moving > 0)
            while (addrs[shownLine] == addrs[shownLine + 1])
                shownLine++;
        shownLine += moving;
    }
    while (shownLine && addrs[shownLine - 1] == addrs[shownLine])
        shownLine--;
    asmAddress = addrs[shownLine];
    getline("", 0);
}

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

void DoDisassembly(HDC dc, RECT *r)
{
    int i;
    COLORREF v;
    int linecnt = (r->bottom - r->top) / 16;
    for (i = 0; i < linecnt; i++)
    {
        int n;
        RECT r1;
		n = addrs[i + shownLine];
        if (addrs[i + shownLine] == addrs[i + shownLine + 1])
        {
            v = SetTextColor(dc, 0xcc00);
        }
        TextOut(dc, ASM_OFFSET + 5, i *16+r->top, lines[i + shownLine], strlen
            (lines[i + shownLine]));
        if (addrs[i + shownLine] == addrs[i + shownLine + 1])
        {
            SetTextColor(dc, v);
        }
		if (strncmp(lines[i + shownLine], "MODULE", 6))
		{
	        if (n == asmIP && (uState == atException || uState == atBreakpoint))
	        {
	            r1.left = 16;
	            r1.top = i * 16;
	            r1.right = r1.left + 16;
	            r1.bottom = r1.top + 16;
	            FillRect(dc, &r1, pcBrush);
	        }
	        if (isBreakPoint(n))
	        {
	            r1.left = 0;
	            r1.top = i * 16;
	            r1.right = r1.left + 16;
	            r1.bottom = r1.top + 16;
	            FillRect(dc, &r1, stopBrush);
	        }
		}
    }
}

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

void doPaint(HWND hwnd)
{
    HDC dc;
    HPEN hpen, oldpen;
    RECT r;
    HBRUSH graybrush;
    PAINTSTRUCT paint;
    HFONT oldFont;
    int oldcolor;
    GetClientRect(hwnd, &r);
    dc = BeginPaint(hwnd, &paint);
    hpen = CreatePen(PS_SOLID, 1, 0xcccccc);
    graybrush = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
    r.right = ASM_OFFSET - 3;
    MoveToEx(dc, ASM_OFFSET - 1, 0, 0);
    LineTo(dc, ASM_OFFSET - 1, r.bottom);
    FillRect(dc, &r, graybrush);
    oldpen = SelectObject(dc, hpen);
    MoveToEx(dc, ASM_OFFSET - 2, 0, 0);
    LineTo(dc, ASM_OFFSET - 2, r.bottom);
    SelectObject(dc, oldpen);
    DeleteObject(hpen);
	DeleteObject(graybrush);
	
    if (DebugProcess.hProcess)
    {
        oldFont = SelectObject(dc, asmFont);
        oldcolor = SetTextColor(dc, 0xff0000);
        DoDisassembly(dc, &r);
        SetTextColor(dc, oldcolor);
        SelectObject(dc, oldFont);
    }
    EndPaint(hwnd, &paint);
}

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

LRESULT CALLBACK _export ASMBlankProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    static int offset;
    RECT r;
    int xlines;
    switch (iMessage)
    {
        case WM_CREATE:
            SetScrollRange(hwnd, SB_VERT, 0, 64000, FALSE);
            SetScrollPos(hwnd, SB_VERT, 32000, TRUE);
            break;
        case WM_PAINT:
            doPaint(hwnd);
            return 0;
        case WM_SETFOCUS:
            CreateCaret(hwnd, 0, 2, 16);
            SetCaretPos(caretX, caretY);
            ShowCaret(hwnd);
            break;
        case WM_KILLFOCUS:
            DestroyCaret();
            break;
        case WM_RBUTTONDOWN:
        case WM_LBUTTONDOWN:
		case WM_LBUTTONDBLCLK:
            caretX = LOWORD(lParam);
            caretY = HIWORD(lParam);
            if (LOWORD(lParam) >= ASM_OFFSET + 5)
            {
                caretY &= ~15;
				if (caretX < ASM_OFFSET + 5)
				{
					caretX = 0;
				}
				else
				{
	                caretX -= ASM_OFFSET + 5;
				}
                caretX /= charwidth;
                caretX *= charwidth;
                caretX += ASM_OFFSET + 5;
                SetCaretPos(caretX, caretY);
            }
            SetFocus(hwnd);
			if (iMessage == WM_LBUTTONDOWN)
			{
				int n ;
setBreakPoint:
	            if (shownLine + caretY / 16 >= 4096)
    	            return 0;
				n = addrs[shownLine + caretY/ 16];
				if (isBreakPoint(n))
				{
					dbgClearBreakPoint(NULL, n);
				}		
				else
				{
					dbgSetBreakPoint(NULL, n, NULL);
				}
				InvalidateRect(hwndASMBlank, 0, 1);
			}
            break;
		case WM_COMMAND:
			if (LOWORD(wParam) == IDM_BREAKPOINT)
				goto setBreakPoint;
			break;
        case WM_KEYDOWN:
            switch (wParam)
            {
            case VK_UP:
                SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
                break;
            case VK_DOWN:
                SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
                break;
            case VK_PRIOR:
                SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
                break;
            case VK_NEXT:
                SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
                break;
            case VK_HOME:
                SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
                break;
            case VK_END:
                SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
                break;
            }
            break;
        case WM_VSCROLL:
            GetClientRect(hwnd, &r);
            xlines = r.bottom / 16;
            switch (LOWORD(wParam))
            {
            case SB_BOTTOM:
            case SB_TOP:
                asmAddress = asmIP;
                CalculateDisassembly(0);
                break;
            case SB_ENDSCROLL:
                return 0;
            case SB_LINEDOWN:
                CalculateDisassembly(1);
                break;
            case SB_LINEUP:
                CalculateDisassembly( - 1);
                break;
            case SB_PAGEDOWN:
                CalculateDisassembly(xlines - 1);
                break;
            case SB_PAGEUP:
                CalculateDisassembly(1-xlines);
                break;
            case SB_THUMBPOSITION:
                offset = 0;
                break;
            case SB_THUMBTRACK:

                xlines = (HIWORD(wParam) - 32000) / 320;
                if (xlines - offset)
                    CalculateDisassembly(xlines - offset);
                offset = xlines;
                InvalidateRect(hwnd, 0, 1);
                return 0;

                //					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
            }
            InvalidateRect(hwnd, 0, 1);
            SetScrollPos(hwnd, SB_VERT, 32000, TRUE);
            return 0;
    }

    return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
}

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

LRESULT CALLBACK _export gotoProc2(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    char buf[256];
    int i;
    VARINFO *info;
    char *types,  *syms;
    int offset = StoppedThread->regs.Eip;
    DEBUG_INFO *dbg;
    switch (iMessage)
    {
        case WM_COMMAND:
            if (wParam == IDOK)
            {
                GetEditField(hwnd, IDC_GOTO, buf);
                info = EvalExpr(&types, &syms, &dbg, offset, &offset, buf, TRUE);
                if (info)
                {
                    if (info->constant)
                        i = info->ival;
                    else
                        i = info->address;
                    FreeVarInfo(info);
                }
                else
                    i = 0xffffffff;
                EndDialog(hwnd, i);
                break;
            }
            if (HIWORD(wParam) == EN_CHANGE)
            {
                DisableControl(hwnd, IDOK, !GetWindowText((HWND)lParam, buf, 2))
                    ;
                break;
            }
            if (wParam != IDCANCEL)
                break;
        case WM_CLOSE:
            EndDialog(hwnd, 0xffffffff);
            break;
        case WM_INITDIALOG:
            CenterWindow(hwnd);
            SetEditField(hwnd, IDC_GOTO, "");
            DisableControl(hwnd, IDOK, 1);
            break;
    }
    return 0;
}

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

LRESULT CALLBACK _export ASMProc(HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    RECT r;
    int i;
    HDC dc;
    TEXTMETRIC metric;
    DEBUG_EVENT *xc;
    HFONT oldFont;
    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(hwndASMBlank);
            }
            return TRUE;
        case WM_SYSCOMMAND:
            if (wParam == SC_CLOSE)
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            break;
        case WM_SETFOCUS:
            SetFocus(hwndASMBlank);
            break;
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
            case IDM_GOTO:
                asmAddress = DialogBox(hInstance, "GOTODIALOG2", hwnd, (DLGPROC)
                    gotoProc2);
                if (asmAddress != 0xffffffff)
                {
                    CalculateDisassembly(0);
                    InvalidateRect(hwndASMBlank, 0, 1);
                }
                break;
            case ID_SETADDRESS:
                asmAddress = asmIP = lParam;
				threadID = StoppedThread->idThread;
                CalculateDisassembly(0);
                InvalidateRect(hwndASMBlank, 0, 1);
                break;
            default:
                return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
            }
            break;
        case WM_GETCURSORADDRESS:
            if (shownLine + caretY / 16 >= 4096)
                return 0;
            sscanf(lines[shownLine + caretY / 16], "%x", &i);
            return i;
        case WM_CREATE:
            lines = (char **)calloc(1,LINES *sizeof(char*));
            if (!lines)
                return  - 1;
            addrs = (int*)calloc(1,LINES *sizeof(int));
            if (!addrs)
                return  - 1;
            for (i = 0; i < LINES; i++)
                if (!(lines[i] = (char*)calloc(1,LINELEN)))
                    return  - 1;
            caretY = 0;
            caretX = ASM_OFFSET + 5;
            hwndASM = hwnd;
            asmFont = CreateFontIndirect(&fontdata);
            dc = GetDC(hwnd);
            oldFont = SelectObject(dc, asmFont);
            GetTextMetrics(dc, &metric);
            SelectObject(dc, oldFont);
            ReleaseDC(hwnd, dc);
            charwidth = metric.tmMaxCharWidth;
            GetClientRect(hwnd, &r);
            hwndCtrl = CreateControlWindow(DID_ASMWND, hwnd, &r, (int)(
                (LPMDICREATESTRUCT)(*(int*)lParam))->lParam, szASMTitle);
            SendMessage(hwndCtrl, LCF_ADJUSTRECT, 0, (LPARAM) &r);
            hwndASMBlank = CreateWindow(szASMBlankClassName, 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_ASMWND, TRUE);
            return 0;
            break;
        case WM_DESTROY:
            dmgrRemoveClient((CCW_params*)GetWindowLong(hwndCtrl, 0));
            DestroyWindow(hwndASMBlank);
            hwndASM = 0;
            DeleteObject(asmFont);
            for (i = 0; i < LINES; i++)
                free(lines[i]);
            free(lines);
            free(addrs);
            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(hwndASMBlank, r.left, r.top, r.right - r.left, r.bottom -
                r.top, TRUE);
            break;
        default:
            break;
    }
    return DefMDIChildProc(hwnd, iMessage, wParam, lParam);
}

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

void RegisterASMWindow(void)
{
    WNDCLASS wc;
    LOGBRUSH brushstr;
    memset(&wc, 0, sizeof(wc));
    wc.style = 0;
    wc.lpfnWndProc = &ASMProc;
    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 = szASMClassName;
    RegisterClass(&wc);

	wc.style = CS_DBLCLKS;
    wc.lpfnWndProc = &ASMBlankProc;
    wc.lpszClassName = szASMBlankClassName;
    RegisterClass(&wc);

    pcBitmap = LoadImage(hInstance, "ID_PCBMP", IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
    stopBitmap = LoadImage(hInstance, "ID_STOPBMP", IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
    brushstr.lbStyle = BS_PATTERN;
    brushstr.lbHatch = (int)pcBitmap;
    pcBrush = CreateBrushIndirect(&brushstr);
    brushstr.lbHatch = (int)stopBitmap;
    stopBrush = CreateBrushIndirect(&brushstr);
}

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

HWND CreateASMWindow(void)
{
    MDICREATESTRUCT mc;
    HWND rv;
    if (hwndASM)
    {
        SendMessage(hwndASM, WM_SETFOCUS, 0, 0);
        return hwndASM;
    }
    mc.szClass = szASMClassName;
    mc.szTitle = szASMTitle;
    mc.hOwner = hInstance;
    mc.x = CW_USEDEFAULT;
    mc.y = CW_USEDEFAULT;
    mc.cx = 500;
    mc.cy = 200;
    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;
}
