/************************************************************************\
 * File Version Information
 * $Header: /Altair32v2/windbg_cmd.c 28    11/08/06 9:21p Racini $
 ************************************************************************/
/************************************************************************\
  MITS Altair Emulator
  Debugger command entry window
  
  Main debugger support for the Solace Sol-20 emulator
  Copyright (c) Jim Battle, 2000-2002

  Modifications for the Altair32 Emulator
  Copyright (c) 2001-2006 Richard A. Cini

  Simply supplies a one-line high subwindow that contains
  command history. The history is accessed by the up and down
  arrow keys. As a bonus, when scrolling through the history,
  redundant commands (one identical to a command already scrolled
  past) are skipped over. This is especially useful after performing
  the STEP command dozens of times.


Change Log:
  2001/11/07  RAC -- Initial changes
  2001/12/14  RAC -- RELEASE MARKER -- v2.1
  2002/01/02  RAC -- Synched with Solace 3.0
  2002/01/31  RAC -- RELEASE MARKER -- v2.2
  2002/05/17  RAC -- Minor mods to sync with Solace v3.1 (to fix "BELL on
  						<ENTER>" issue)
  2002/07/07  RAC -- RELEASE MARKER -- v2.3
  2002/08/23  RAC -- RELEASE MARKER -- v2.30.10
  2002/11/15  RAC -- RELEASE MARKER -- v2.40.2100
  2003/04/26  RAC -- RELEASE MARKER -- v2.50.2045
  2004/01/20  RAC -- Diff'ed changes from FJS (Fred J. Scipione)
  2004/07/30  RAC -- RELEASE MARKER -- v3.00.0135
  2006/05/12  RAC -- RELEASE MARKER -- v3.10.0200
  2006/11/15  RAC -- RELEASE MARKER -- v3.20.0400
\************************************************************************/
#include <windows.h>
#include <windowsx.h>	// message cracker macros
#include <stdlib.h>
#include <stdio.h>		// C-lib - I/O
#include <string.h>
#include "altair32.h"
#include "extensions.h" // corrections for bad functions in windowsx
#include "windbg_sub.h"

#define MAX_CMD_LEN   200	// max # of chars in command string
#define HIST_LINES     50	// max # of lines in cmd window
#define SKIP_REDUNDANT  1	// 1=skip commands, 0=show all commands


static struct tagCMDSTATE {
    HWND hEdit;			// handle to edit window
    int lines;			// # of commands in history log
    int curline;		// which line we are recalling
    char *text[HIST_LINES];	// text for previous commands
} cmdstate;


// subclass handler for command edit control.
// this subclass looks at all the characters going into the
// edit box and causes side effects if one of the following keys
// is pressed:
//
//             <CR> send command to debug interpreter
//        <UPARROW> go back in command history
//      <DOWNARROW> go forward in command history
//
// Everything else gets passed through.

static const char *CmdEditSubclassName = "solace.cmdedit";
static ATOM CmdEditSubclassAtom;


// --- exported function ---
//
// the edit history is kept as a table of string pointers.
// while a new command is being edited, it is kept in the
// last entry of the list; this is so we can go back and
// forth through the history and come back to the partially
// edited line.
void
InitEditHist(void)
{
    int i;

    // for subclassing the command window EDIT control
    CmdEditSubclassAtom = GlobalAddAtom(CmdEditSubclassName);

    for(i=0; i<HIST_LINES; i++)
	cmdstate.text[i] = NULL;

    cmdstate.lines   = 0;
    cmdstate.curline = 0;
}


static void
DestroyEditHist(void)
{
    int i;
    for(i=0; i<HIST_LINES; i++)
	if (cmdstate.text[i] != NULL) {
	    free(cmdstate.text[i]);
	    cmdstate.text[i] = NULL;
	}
    cmdstate.lines   = 0;
    cmdstate.curline = 0;
}


static void
AddEditHist(char *cmd)
{
    int i;

    if (cmdstate.lines == HIST_LINES) {
	// remove oldest line from history
	free(cmdstate.text[0]);
	for(i=0; i<HIST_LINES-1; i++) {
	    // yeah, a linked list would be better, but this is easy
	    cmdstate.text[i] = cmdstate.text[i+1];
	}
	cmdstate.lines--;
	cmdstate.text[cmdstate.lines] = NULL;
    }

    cmdstate.text[cmdstate.lines] = strdup(cmd);
    cmdstate.curline = cmdstate.lines;
    cmdstate.lines++;
}


// return a pointer to a command string n ahead (+) or behind (-)
// the curline entry.
static char *
GetEditHist(int *n, int dir)
{
    int line = *n;

    while (1) {

	int found;

	line += dir;

	if ((line < 0) || (line >= cmdstate.lines))
//	    return NULL;	// DELETED FJS
		break;			// ADDED FJS

	// check to see if we have seen this command in the
	// history log already; if so, skip it
	found = 0;
#if SKIP_REDUNDANT
	{ int i;
	    for(i=line+1; i < cmdstate.lines; i++) {
		if (!strcmp(cmdstate.text[line], cmdstate.text[i]))
		    { found = 1; break; }
	    }
	}
#endif

	if (!found) {
	    *n = line;
	    return cmdstate.text[line];
	}
    }
	return NULL; // FJS
}


// set the final edit history command.  this is used when
// the cursor keys have been used to navigate edit history,
// and overwrites the edit-line-in-progress that was there.
static void
FinalEditHist(char *cmd)
{
    int n = cmdstate.lines-1;
    free(cmdstate.text[n]);
    cmdstate.text[n] = strdup(cmd);
}


static LRESULT CALLBACK
CmdEditSubclass(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	
	WNDPROC oldproc; // FJS
    char cmdstr[MAX_CMD_LEN];
    char *hist;
    static int new_cmd = 1;
    int dir = +1;	// used for VK_UP vs VK_DOWN


//   oldproc = (WNDPROC)(GetProp(hwnd, ((LPCTSTR)CmdEditSubclassAtom))); // FJS
    oldproc = (WNDPROC)(GetProp(hwnd, ((LPCTSTR)CmdEditSubclassName))); // GCC 
    switch (iMsg) {

	case WM_GETDLGCODE:
	    return DLGC_WANTALLKEYS
		    | CallWindowProc(oldproc, hwnd, iMsg, wParam, lParam);

	case WM_CHAR:
	    // Process this message to avoid message beeps
	    if ((wParam == VK_RETURN) || (wParam == VK_TAB))
		return 0;
	    else
		return (CallWindowProc(oldproc, hwnd, iMsg, wParam, lParam));

	case WM_KEYDOWN:
	    switch (wParam) {

		case VK_RETURN:

		    GetWindowText(hwnd, cmdstr, sizeof(cmdstr));
		    SetWindowText(hwnd, "");	// blank out message

		    if (new_cmd) AddEditHist(cmdstr);
			    else FinalEditHist(cmdstr);

		    if (strlen(cmdstr) == 0) {
			// recycle previous command
			int n = cmdstate.lines - 1;
			hist = GetEditHist(&n, -1);
			if (hist != NULL) {
			    strcpy(cmdstr, hist);
			    FinalEditHist(cmdstr);
			}
		    }

		    UI_DbgWinLog(cmdstr, TRUE);	// bold message
		    dbg_interp(cmdstr);		// interpret it
		    new_cmd = 1;
		    return 0;			// claim we handled it

		case VK_UP:
		    dir = -1;
		    // fall through ...
		case VK_DOWN:
		    // fetch prev/next command from command history
		    // make a temporary final edit entry if we haven't yet
		    GetWindowText(hwnd, cmdstr, sizeof(cmdstr));
		    if (new_cmd)
			AddEditHist(cmdstr);
		    else if (cmdstate.curline == cmdstate.lines-1)
			FinalEditHist(cmdstr);
		    hist = GetEditHist(&cmdstate.curline, dir);
		    if (hist != NULL) {
			int len = strlen(hist);
			SetWindowText(hwnd, hist);	// blank out message
			Edit_SetSel(hwnd, len, len);	// move caret to end of line
		    }
		    new_cmd = 0;
		    return 0;	// claim we handled it

		case VK_F5:
		case VK_F6:
		case VK_F7:
		    handle_fkey((UINT)wParam);
		    return 0;

		default:
		    break;
	    }
	    break;

	case WM_DESTROY:
	    SetWindowLong(hwnd, GWL_WNDPROC, (LONG)oldproc);
//            RemoveProp(hwnd, (LPCTSTR)CmdEditSubclassAtom);
	    RemoveProp(hwnd, (LPCTSTR)CmdEditSubclassName); // GCC
	    return 0;

	default:
	    break;
    }


    // call original process
    return CallWindowProc(oldproc, hwnd, iMsg, wParam, lParam);
}


// this edit control ID can be any value so long as it is
// unique for all children of the parent window.  since this
// window has only one child, it can be anything.
#define EDITWIN_CTL_ID 0x5

static BOOL
OnCreateCmd(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
    DWORD winstyle = WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL;
    HWND editwin;

    // open up debugging container window
    editwin = CreateWindow(
		"edit",			// window class name
		NULL,			// window caption
		winstyle,		// window style
		CW_USEDEFAULT, 0,	// initial x,y position
		CW_USEDEFAULT, 0,	// initial x,y size
		hwnd,			// parent window handle
		(HMENU)EDITWIN_CTL_ID,	// control ID
		winstate.hInst,		// program instance handle
		NULL);			// creation parameters

    ASSERT(editwin != NULL);

    // used fixed-pitch font in edit window
    SendMessage(editwin, WM_SETFONT,
			(WPARAM)dbgstate.fixed_font, (LPARAM)TRUE);

    // limit the number of chars of input may be entered
    Edit_LimitText(editwin, MAX_CMD_LEN);
    
    // install subclass handler for this specific edit control
    {
	WNDPROC old = (WNDPROC)GetWindowLong(editwin, GWL_WNDPROC);
	SetWindowLong(editwin, GWL_WNDPROC, (LONG)CmdEditSubclass);
//        SetProp(editwin, (LPCTSTR)CmdEditSubclassAtom, (HANDLE)old);
	SetProp(editwin, (LPCTSTR)CmdEditSubclassName, (HANDLE)old); // GCC
    }

    cmdstate.hEdit = editwin;
    InitEditHist();

    return TRUE;
}


// handle window resize events
static void
OnSizeCmd(HWND hwnd, UINT state, int cx, int cy)
{
    MoveWindow(cmdstate.hEdit, 0, 0, cx, cy, TRUE);
}


static void
OnDestroyCmd(HWND hwnd)
{
    DestroyEditHist();
    dbgstate.hCmdWnd = NULL;
    cmdstate.hEdit   = NULL;
}


// debugger command window handler
static LRESULT CALLBACK
WndProcCmd(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    switch (iMsg) {
	HANDLE_MSG(hWnd, WM_CREATE,  OnCreateCmd);
	HANDLE_MSG(hWnd, WM_SIZE,    OnSizeCmd);
	HANDLE_MSG(hWnd, WM_DESTROY, OnDestroyCmd);

	case WM_SETFOCUS:
	    SetFocus(cmdstate.hEdit);
	    break;
    }

    return DefWindowProc(hWnd, iMsg, wParam, lParam);
}


// forward a character message received by one of the other subwindows
// to the command subwindow, and redirect focus to said subwindow.
// it worked to SendMessage() the message to dbgstate.hCmdWnd, and from
// there catch WM_CHAR and SendMessage() it to the edit subwindow, but
// it seemed dangerous, so instead this message forwards it directly.
void
windbg_cmd_fwdchar(WPARAM wParam, LPARAM lParam)
{
    SetFocus(cmdstate.hEdit);
    SendMessage(cmdstate.hEdit, WM_CHAR, wParam, lParam);
}


// --- exported function ---
void
RegisterCmdClass(void)
{
    WNDCLASSEX  wndclass;

    wndclass.cbSize        = sizeof(wndclass);
    wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wndclass.lpfnWndProc   = WndProcCmd;
    wndclass.cbClsExtra    = 0;
    wndclass.cbWndExtra    = 0;
    wndclass.hInstance     = winstate.hInst;
    wndclass.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor       = NULL; //LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wndclass.lpszMenuName  = NULL;
    wndclass.lpszClassName = "solacecmd";
    wndclass.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    RegisterClassEx(&wndclass);
}


// --- exported function ---
HWND
CreateCmdWindow(HWND hwnd)
{
    DWORD winstyle = WS_CHILD | WS_VISIBLE;
    DWORD extstyle = WS_EX_CLIENTEDGE;
    HWND  hwndcmd;

    hwndcmd = CreateWindowEx(
		extstyle,		// extended style
		"solacecmd",		// window class name
		0,			// window caption
		winstyle,		// window style
		0,0,			// initial x,y position
		0,0,			// initial x,y size
		hwnd,			// parent window handle
		NULL,			// window menu handle
		winstate.hInst,		// program instance handle
		NULL);			// creation parameters

    ASSERT(hwndcmd != NULL);

    return hwndcmd;
}
/* end of file: windbg_cmd.c */
