/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   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., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
**********************************************************************

STACKWND.C unrolls the stack, and shows functions called at a level
higher than the breakpoint (this thread only)

**********************************************************************
*/
#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 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 ;
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 -rect.top)/ 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+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 ;
}
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 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) ;
         }
         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 ;
   }
   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_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) ;
         SendMessage(hwndCtrl,LCF_ADJUSTRECT,0,(LPARAM)&r) ;
         hwndBlank = CreateWindow(szStackBlankClassName,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) ;
         StackFont = CreateFontIndirect(&fontdata) ;
         wParam = 1 ;
         // fall through
      case WM_RESTACK:
         if (uState == notDebugging || !wParam) 
            ClearStackArea(hwnd) ;
         else
            SetStackArea(hwnd) ;
         SetScrollRange(hwndBlank,SB_VERT,StackAddress = 0,StackLines *1,FALSE );
         SetScrollPos(hwndBlank,SB_VERT,0,TRUE) ;
         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 = "Stack Window" ;
	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 ;
}