#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

LRESULT  CALLBACK _export YesNoProc( HWND hwnd, UINT iMessage, WPARAM wParam,
                                 LPARAM lParam ) ;
extern HINSTANCE hInstance ;
extern HWND hwndClient,hwndStatus,hwndFrame ;
extern int childxpos, childypos ;
extern PROCESS DebugProcess ;
extern enum DebugState uState;

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

static char *lines[4096] ;
static int curline, shownLine ;

static char szASMClassName[] = "xccASMClass";
static int asmAddress = 0x401000, asmIP, lastAddress = 0 ;
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 ;

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;
}
int CreateDisassembly(void)
{
	int rv = 0 ;
	char buffer[256 ];
	BYTE dbuf[4096] ;
	int i =0xffffffff;
	code_ptr = dbuf ;
	for (i=0; i < 4096; i++) {
		free(lines[i]) ;
		lines[i] = 0 ;
	}
	i = 0 ;
	if (asmAddress - lastAddress > 2048-16 || asmAddress - lastAddress < 0)
			lastAddress = asmAddress-2048 ;
	if (!ReadProcessMemory(DebugProcess.hProcess, (LPVOID) lastAddress,dbuf,4096,0))
		ReadProcessMemory(DebugProcess.hProcess, (LPVOID) (lastAddress = asmAddress & 0xffff0000) ,dbuf,4096,0) ;

	code_address = lastAddress ;
  while (code_ptr < dbuf+4096-16) {
		if (code_address == asmAddress)
			rv = i ;
    GetCodeLine(buffer);
		lines[i] = malloc(strlen(buffer)+1) ;
		strcpy(lines[i++],buffer) ;
  }
	return rv ;
}
void DoDisassembly(HDC dc, RECT *r)
{
	int i ;
	int linecnt = (r->bottom - r->top)/16 ;
	memset(lines,0,sizeof(char *)*4096) ;
	curline = CreateDisassembly() ;
		if (curline >= linecnt)
			shownLine = curline-linecnt/2 ;
		else
			shownLine = 0 ;
	sscanf(lines[shownLine],"%x",&lastAddress) ;
	for (i=0; i < linecnt; i++) {
		int n ;
		RECT r1 ;
		sscanf(lines[i+shownLine], "%x",&n) ;
		TextOut(dc,ASM_OFFSET+5,i*16,lines[i+shownLine],strlen(lines[i+shownLine])) ;
		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 = GetStockObject(LTGRAY_BRUSH) ;
			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) ;

			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 ASMProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
	static int charwidth ;
	int i ;
	HDC dc ;
	TEXTMETRIC metric ;
	DEBUG_EVENT *xc ;
	HFONT oldFont ;
	RECT r ;
	int xlines ;
	switch(iMessage) {
		case WM_SYSCOMMAND :
			if (wParam == SC_CLOSE)
				SendMessage(hwnd,WM_CLOSE,0,0) ;
			break ;
		case WM_COMMAND:
			switch(LOWORD(wParam)) {
            case IDM_GOTO:
					asmAddress = lParam ;
					rePosition = TRUE ;
					InvalidateRect(hwnd,0,1) ;
					break ;
				case ID_SETADDRESS:
					xc = (DEBUG_EVENT *)lParam ;
					asmAddress = asmIP = xc->u.Exception.ExceptionRecord.ExceptionAddress ;
					threadID = xc->dwThreadId ;
					rePosition = TRUE ;
					InvalidateRect(hwnd,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_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_LBUTTONDOWN:
			caretX = LOWORD(lParam) ;
			caretY = HIWORD(lParam) ;
			if (LOWORD(lParam) >= ASM_OFFSET + 5) {
				caretY &= ~15 ;
				caretX -= ASM_OFFSET + 5 ;
				caretX /= charwidth ; caretX *= charwidth ;
				caretX += ASM_OFFSET + 5 ;
				SetCaretPos(caretX, caretY) ;
			}
			break ;
		case WM_CREATE:
			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 ;
			SetScrollRange(hwnd,SB_VERT,0,64000,FALSE );
			SetScrollPos(hwnd,SB_VERT,32000,TRUE) ;
         break ;
		case WM_VSCROLL:
			GetClientRect(hwnd,&r) ;
			xlines = r.bottom/16 ;
			switch(LOWORD(wParam)) {
				case SB_BOTTOM:
				case SB_TOP:
					asmAddress = asmIP ;
					break ;
				case SB_ENDSCROLL:
					return 0 ;
				case SB_LINEDOWN:
					if (shownLine == 0)
						sscanf(lines[xlines/2+1],"%x",&asmAddress) ;
					else
						sscanf(lines[curline+1],"%x",&asmAddress) ;
					lastAddress = 0 ;
					break ;
				case SB_LINEUP:
					if (curline) {
						sscanf(lines[curline-1],"%x",&asmAddress) ;
						lastAddress = 0 ;
					}
					break ;
				case SB_PAGEDOWN:
					sscanf(lines[curline+xlines-1],"%x",&asmAddress) ;
					lastAddress = 0 ;
					break ;
				case SB_PAGEUP:
					if (curline >= xlines-1) {
						sscanf(lines[curline-xlines+1],"%x",&asmAddress) ;
						lastAddress = 0 ;
					} else
						sscanf(lines[0],"%x",&asmAddress) ;
					break ;
				case SB_THUMBPOSITION:
					break ;
				case SB_THUMBTRACK :
					return 0 ;
//					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
			}
			InvalidateRect(hwnd,0,1) ;
//			SetScrollPos(hwnd,SB_VERT, 32000,TRUE) ;
			return 0 ;
				
		case WM_CLOSE:
			break ;
		case WM_DESTROY:
			hwndASM = 0 ;
			DeleteObject(asmFont) ;
			for (i=0; i < 4096; i++) {
				free(lines[i]) ;
				lines[i] = 0 ;
			}
			break ;
		case WM_SIZE:       
			break ;
		case WM_INITMENUPOPUP:
			return 0 ;
		default: 
			break ;
	}
	return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
}
void RegisterASMWindow(void)
{
		WNDCLASS wc ;
      LOGBRUSH brushstr ;
      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) ;
      pcBitmap = LoadBitmap(hInstance, "ID_PCBMP") ;
      stopBitmap = LoadBitmap(hInstance, "ID_STOPBMP") ;
      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 = "Assembly Window" ;
	mc.hOwner = hInstance ;
	mc.x = childxpos ;
	mc.y = childypos ;
	mc.cx = 0;
	mc.cy = 0;
	mc.style = WS_VSCROLL;
	mc.lParam = 0 ;
	rv = (HWND) SendMessage(hwndClient,WM_MDICREATE,0,(LPARAM)&mc) ;
	childxpos += 20 ;
	childypos += 20 ;
	if (childxpos > 120)
		childxpos = childypos = 0 ;
	return rv ;
}