#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 HINSTANCE hInstance ;
extern HWND hwndClient,hwndStatus,hwndFrame ;
extern PROCESS DebugProcess ;
extern enum DebugState uState;
extern CONTEXT StoppedRegs ;

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

HWND hwndStack ;

static char szStackClassName[] = "xccStackClass" ;

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 ;
void StackDoPaint(HWND hwnd)
{
	int i ;
   STACKLIST *list =StackList;
   int Address = StackAddress ;
		char buf[256],*p ;
		PAINTSTRUCT ps ;
		CONTEXT context ;
		HDC dc ;
		HFONT oldFont ;
		RECT rect ;
		int lines ;
		GetClientRect(hwnd,&rect) ;
		lines = rect.bottom / 16 ;
		dc = BeginPaint(hwnd, &ps) ;
      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,buf,strlen(buf)) ;
           SetTextColor(dc,oldtext) ;
           SetBkColor(dc,oldbk) ;
         } else
            TextOut(dc,0,i*16,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 ;
}
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 ;
}
void SetStackArea(HWND hwnd)
{
   int ebp = StoppedRegs.Ebp ;
   int eip = StoppedRegs.Eip ;
   int len ;
   STACKLIST *stackbase=0, *stackptr, *newstack ;
   char *symtab;
   int offset ;
   char buf[256];
   int lines = 0 ;

   ClearStackArea(hwnd) ;
   StackLines = 0 ;

   if (uState != atBreakpoint && uState != atException)
      return ;
   while (1) {
      eip = eipReal(eip) ;
      FindFunctionName(buf, eip) ;
      newstack = malloc(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++ ;
      ReadProcessMemory(DebugProcess.hProcess,(LPVOID)(ebp+4),(LPVOID) &eip,4, &len ) ;
      if (!eip)
         break ;
      ReadProcessMemory(DebugProcess.hProcess,(LPVOID)ebp,(LPVOID) &ebp,4, &len ) ;
   }
   StackList = stackbase ;
   StackLines = lines ;
}
LRESULT  CALLBACK _export StackProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
	static int charwidth ;
	int i,lines ;
	HDC dc ;
	TEXTMETRIC metric ;
	DEBUG_EVENT *xc ;
	HFONT oldFont ;
	LOGBRUSH brushstr ;
	RECT r ;
   char module[256] ;
   STACKLIST *sl ;
	switch(iMessage) {
		case WM_SYSCOMMAND :
			if (wParam == SC_CLOSE)
				SendMessage(hwnd,WM_CLOSE,0,0) ;
			break ;
		case WM_COMMAND:
			break ;
		case WM_PAINT:
         StackDoPaint(hwnd) ;
			return 0 ;
		case WM_SETFOCUS:
			SendMessage(hwndFrame,WM_REDRAWTOOLBAR,0,0) ;
			break ;
		case WM_KILLFOCUS:
         break ;          
		case WM_CREATE:
         hwndStack = hwnd ;
         StackFont = CreateFontIndirect(&fontdata) ;
         wParam = 1 ;
         // fall through
      case WM_RESTACK:
         if (uState == notDebugging || !wParam) 
            ClearStackArea(hwnd) ;
         else
            SetStackArea(hwnd) ;
         SetScrollRange(hwnd,SB_VERT,StackAddress = 0,StackLines *1,FALSE );
         SetScrollPos(hwnd,SB_VERT,0,TRUE) ;
         InvalidateRect(hwnd,0,0) ;
         break ;
		case WM_CLOSE:
			break ;
		case WM_DESTROY:
         ClearStackArea(hwnd) ;
         hwndStack = 0 ;
         DeleteObject(StackFont) ;
			break ;
		case WM_SIZE:       
			break ;
		case WM_INITMENUPOPUP:
			return 0 ;
      case WM_LBUTTONDOWN:
         lines = HIWORD(lParam) ;
         if (lines/16 + StackAddress < StackLines) {
            curpos = lines/16 ;
            InvalidateRect(hwnd,0,TRUE) ;
         }
         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 ;
                      CreateDrawWindow(&x) ;
                  }
               }
               break ;
            case VK_UP:
               if (curpos)
                  curpos-- ;
               else if (StackAddress)
                  StackAddress-- ;
               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++ ;
                  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:
               StackAddress += (HIWORD(wParam) - 32000) * 1 ;
					break ;
				case SB_THUMBTRACK :
					return 0 ;
//					return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
			}
         if (StackAddress < 0)
            StackAddress = 0 ;
         if (StackAddress >= StackLines)
            StackAddress = StackLines-1 ;
			InvalidateRect(hwnd,0,0) ;
//			SetScrollPos(hwnd,SB_VERT, 32000,TRUE) ;
			return 0 ;
		default: 
			break ;
	}
	return DefMDIChildProc(hwnd,iMessage,wParam,lParam) ;
}
void RegisterStackWindow(void)
{
		WNDCLASS 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) ;
}
HWND CreateStackWindow(void)
{
	MDICREATESTRUCT mc ;
	HWND rv ;
	RECT r ;
   if (hwndStack) {
      SendMessage(hwndStack,WM_SETFOCUS,0,0) ;
      return hwndStack ;
	}
	GetClientRect(hwndClient,&r) ;
   mc.szClass = szStackClassName ;
   mc.szTitle = "Stack Window" ;
	mc.hOwner = hInstance ;
	mc.x =  CW_USEDEFAULT;
	mc.y =  CW_USEDEFAULT;
   mc.cx = 30*8;
	mc.cy = 19*8;
	mc.style = WS_VSCROLL ;
	mc.lParam = 0 ;
   rv = (HWND) SendMessage(hwndClient,WM_MDICREATE,0,(LPARAM)&mc) ;
	return rv ;
}