// --------------------------------------------------------------------------
// Citadel: ScriptDB.CPP
//
// The script debugger and profiler

#include "ctdl.h"
#pragma hdrstop

#include "script.h"

#include "msg.h"
#include "log.h"

// --------------------------------------------------------------------------
// Contents


// Debugging stuff.
Bool dwHandler(EVENT evt, long param, int more, CITWINDOW *wnd);
static Bool wwHandler(EVENT evt, long param, int more, CITWINDOW *wnd);
static Bool swHandler(EVENT evt, long param, int more, CITWINDOW *wnd);

struct swLocal
	{
	int ThisLine, LastLine;
	strList *FileData, *ThisLineData;
	};



void DebuggingMsg(const char *Msg)
	{
	assert(CurScript->DebuggingWindow);
	assert(CurScript->DebuggingWindow->LocalData);

	memmove(CurScript->DebuggingWindow->LocalData,
			((char *) CurScript->DebuggingWindow->LocalData) + (conCols - 2),
			(conRows / 2 - 2) * (conCols - 2));

	memset(((char *) CurScript->DebuggingWindow->LocalData) + (conRows / 2 - 2) *
			(conCols - 2), 0, conCols - 2);

	if (*Msg)
		{
		memcpy(((char *) CurScript->DebuggingWindow->LocalData) + (conRows / 2 - 2) *
			(conCols - 2), Msg, strlen(Msg));
		}

	(CurScript->DebuggingWindow->func)(EVT_DRAWINT, 0, 0, CurScript->DebuggingWindow);
	}

enum
	{
	CTRL_EVALEXP,	CTRL_BPLINE,	CTRL_WATCHEXP,	CTRL_ABORT,
	CTRL_QUIT,
	};

static Bool HandleDebuggingKeyboardCommand(int Key)
	{
	switch (toupper(Key))
		{
		case ' ':
			{
			if (CurScript->Pause)
				{
				CurScript->SingleStep = TRUE;
				DebuggingMsg(getscrmsg(41));
				}

			return (TRUE);
			}

		case 'B':
			{
			CitWindowsGetString(getscrmsg(42), 10, ns, CurScript->DebuggingWindow,
					CTRL_BPLINE, NULL, FALSE);
			return (TRUE);
			}

		case 'E':
			{
			CitWindowsGetString(getscrmsg(43), 60, ns, CurScript->DebuggingWindow, 
					CTRL_EVALEXP, NULL, FALSE);
			return (TRUE);
			}

		case 'P':
			{
			DebuggingMsg(getscrmsg(44));
			CurScript->Pause = TRUE;
			return (TRUE);
			}

		case 'Q':
			{
			CitWindowsGetYN(getscrmsg(48), 0, CurScript->DebuggingWindow, CTRL_QUIT,
					NULL);
			return (TRUE);
			}

		case 'S':
			{
			if (!CurScript->SourceWindow)
				{
				swLocal *sw = new swLocal;

				if (sw)
					{
					memset(sw, 0, sizeof(*sw));

					WINDOWFLAGS flags;
					SRECT rect;

					memset(&flags, 0, sizeof(flags));

					flags.visible = TRUE;
					flags.showTitle = TRUE;
					flags.moveable = TRUE;
					flags.resize = TRUE;
					flags.minimize = TRUE;
					flags.maximize = TRUE;
					flags.close = TRUE;

					rect.top = 0;
					rect.left = 0;
					rect.bottom = conRows / 4;
					rect.right = conCols - 1;

					CurScript->SourceWindow = makeCitWindow(swHandler, CurScript->DebuggingWindow,
							getscrmsg(49), flags, rect, sw, FALSE);
					}
				}

			return (TRUE);
			}

		case 'U':
			{
			DebuggingMsg(getscrmsg(45));
			CurScript->Pause = FALSE;
			return (TRUE);
			}

		case 'W':
			{
			CitWindowsGetString(getscrmsg(46), 60, ns,
					CurScript->DebuggingWindow, CTRL_WATCHEXP, NULL, FALSE);
			return (TRUE);
			}

		case 'X':
			{
			CitWindowsGetYN(getscrmsg(47), 0, CurScript->DebuggingWindow,
					CTRL_ABORT, NULL);
			return (TRUE);
			}
		}

	return (FALSE);
	}

static Bool swHandler(EVENT evt, long param, int more, CITWINDOW *wnd)
	{
	switch (evt)
		{
		case EVT_INKEY:
			{
			if (param <= 255)
				{
				if (HandleDebuggingKeyboardCommand((int) param))
					{
					break;
					}
				}
			else
				{
				switch (param >> 8)
					{
					case CURS_UP:
						{
						(wnd->func)(EVT_DRAWINT, 0, 3, wnd);
						return (TRUE);
						}

					case CURS_DOWN:
						{
						(wnd->func)(EVT_DRAWINT, 0, 2, wnd);
						return (TRUE);
						}

					case CURS_HOME:
						{
						(wnd->func)(EVT_DRAWINT, 0, 1, wnd);
						return (TRUE);
						}
					}
				}

			return (defaultHandler(evt, param, more, wnd));
			}

		case EVT_NEWWINDOW:
			{
			FILE *fl;
			if ((fl = fopen(CurScript->Name, FO_R)) != NULL)
				{
				char string[128];
				int ThisLine = 0;

				while (fgets(string, 128, fl))
					{
					if (string[strlen(string) - 1] == '\n')
						{
						string[strlen(string) - 1] = 0;
						}

					strList *sl = (strList *) addLL((void **) &((swLocal *)
							(wnd->LocalData))->FileData,
							sizeof(*sl) + strlen(string));

					if (sl)
						{
						ThisLine++;
						strcpy(sl->string, string);
						}
					else
						{
						break;
						}
					}

				((swLocal *) (wnd->LocalData))->LastLine = ThisLine;
				((swLocal *) (wnd->LocalData))->ThisLine = 1;
				((swLocal *) (wnd->LocalData))->ThisLineData =
						((swLocal *) (wnd->LocalData))->FileData;

				fclose(fl);
				}
			else
				{
				strList *sl = (strList *) addLL((void **) &((swLocal *)
						(wnd->LocalData))->FileData,
						sizeof(*sl) + strlen(getscrmsg(50)));

				if (sl)
					{
					strcpy(sl->string, getscrmsg(50));
					}
				}

			break;
			}

		case EVT_DESTROY:
			{
			disposeLL((void **) &((swLocal *) (wnd->LocalData))->FileData);
			CurScript->SourceWindow = NULL;
			break;
			}

		case EVT_DRAWINT:
			{
			int LinesVisible = wnd->extents.bottom - wnd->extents.top - 1;

			if (LinesVisible && wnd->flags.visible)
				{
				if (buildClipArray(wnd))
					{
					Bool Changed = FALSE;

					if (more == 1)
						{
						// Find current command line

						if (CurScript->curCmd->line >=
								((swLocal *) (wnd->LocalData))->ThisLine +
								LinesVisible)
							{
							((swLocal *) (wnd->LocalData))->ThisLine =
									CurScript->curCmd->line -
									(LinesVisible / 2);
							Changed = TRUE;
							}

						if (CurScript->curCmd->line <
								((swLocal *) (wnd->LocalData))->ThisLine)
							{
							((swLocal *) (wnd->LocalData))->ThisLine =
									CurScript->curCmd->line;
							Changed = TRUE;
							}
						}
					else if (more == 2)
						{
						// Down
						((swLocal *) (wnd->LocalData))->ThisLine++;
						Changed = TRUE;
						}
					else if (more == 3)
						{
						// Up
						((swLocal *) (wnd->LocalData))->ThisLine--;
						Changed = TRUE;
						}

					int CurLine = ((swLocal *) (wnd->LocalData))->ThisLine;

					strList *w;

					if (Changed)
						{
						w = (strList *) getLLNum(((swLocal *)
								(wnd->LocalData))->FileData, CurLine);
						((swLocal *) (wnd->LocalData))->ThisLineData = w;
						}
					else
						{
						w = ((swLocal *) (wnd->LocalData))->ThisLineData;
						}

					for (int i = 1;
							w && i < wnd->extents.bottom - wnd->extents.top;
							CurLine++, i++, w = (strList *) getNextLL(w))
						{
						CitWindowClearLine(wnd, i, cfg.attr);

						label LNum;
						sprintf(LNum, pctd, CurLine);

						Bool IsBP = FALSE;

						for (Breakpoints *bp = CurScript->BPoints; bp;
								bp = (Breakpoints *) getNextLL(bp))
							{
							if (bp->LineNum == CurLine)
								{
								IsBP = TRUE;
								break;
								}
							}

						CitWindowOutStr(wnd, 1, i, LNum, IsBP ?
								cfg.cattr : cfg.attr);
						CitWindowOutStr(wnd, 5, i, w->string,
								CurLine == CurScript->curCmd->line ?
								cfg.wattr : cfg.attr);
						}

					for (; i < wnd->extents.bottom - wnd->extents.top; i++)
						{
						CitWindowClearLine(wnd, i, cfg.attr);
						}

					freeClipArray();
					}
				}

			break;
			}

		default:
			{
			return (defaultHandler(evt, param, more, wnd));
			}
		}

	return (TRUE);
	}

static Bool wwHandler(EVENT evt, long param, int more, CITWINDOW *wnd)
	{
	switch (evt)
		{
		case EVT_INKEY:
			{
			if (param <= 255)
				{
				if (HandleDebuggingKeyboardCommand((int) param))
					{
					break;
					}
				}

			return (defaultHandler(evt, param, more, wnd));
			}

		case EVT_DESTROY:
			{
			disposeLL((void **) &wnd->LocalData);
			CurScript->WWindow = NULL;
			break;
			}

		case EVT_DRAWINT:
			{
			if (wnd->flags.visible)
				{
				if (buildClipArray(wnd))
					{
					strList *w;
					int i;

					for (i = 1, w = (strList *) wnd->LocalData;
							w && i < wnd->extents.bottom - wnd->extents.top;
							i++, w = (strList *) getNextLL(w))
						{
						label showWatch;
						char watchValue[256], display[90];

						CopyStringToBuffer(showWatch, w->string, LABELSIZE);
						strcpy(watchValue, evaluateString(w->string));
						watchValue[50] = 0;

						sprintf(display, getmsg(1560), showWatch, watchValue);

						CitWindowClearLine(wnd, i, cfg.attr);
						CitWindowOutStr(wnd, 1, i, display, cfg.attr);
						}

					for (; i < wnd->extents.bottom - wnd->extents.top; i++)
						{
						CitWindowClearLine(wnd, i, cfg.attr);
						}

					freeClipArray();
					}
				}

			break;
			}

		default:
			{
			return (defaultHandler(evt, param, more, wnd));
			}
		}

	return (TRUE);
	}

static void CreateWatchWindow(void)
	{
	if (!CurScript->WWindow)
		{
		WINDOWFLAGS flags;
		SRECT rect;

		memset(&flags, 0, sizeof(flags));

		flags.visible = TRUE;
		flags.showTitle = TRUE;
		flags.moveable = TRUE;
		flags.minimize = TRUE;
		flags.maximize = TRUE;
		flags.resize = TRUE;

		rect.top = 0;
		rect.left = 0;
		rect.bottom = 2;
		rect.right = conCols / 2;

		CurScript->WWindow = makeCitWindow(wwHandler, CurScript->DebuggingWindow,
				getscrmsg(36), flags, rect, NULL, FALSE);
		}
	}

static strList *findWatch(const char *Expr)
	{
	strList *w = NULL;

	if (CurScript->WWindow)
		{
		for (w = (strList *) CurScript->WWindow->LocalData; w;
				w = (strList *) getNextLL(w))
			{
			if (SameString(w->string, Expr))
				{
				break;
				}
			}
		}

	return (w);
	}

Bool RemoveWatch(const char *Expr)
	{
	if (CurScript->WWindow)
		{
		strList *w;
		int i;

		for (i = 1, w = (strList *) CurScript->WWindow->LocalData; w;
				w = (strList *) getNextLL(w), i++)
			{
			if (SameString(w->string, Expr))
				{
				break;
				}
			}

		if (w)
			{
			deleteLLNode((void **) &CurScript->WWindow->LocalData, i);

			if (!CurScript->WWindow->LocalData)
				{
				destroyCitWindow(CurScript->WWindow, FALSE);
				CurScript->WWindow = NULL;
				}
			else
				{
				(CurScript->WWindow->func)(EVT_DRAWINT, 0, 0, CurScript->WWindow);
				}

			return (TRUE);
			}
		else
			{
			return (FALSE);
			}
		}
	else
		{
		return (FALSE);
		}
	}

Bool AddWatch(const char *Expr)
	{
	CreateWatchWindow();

	if (CurScript->WWindow)
		{
		strList *w = (strList *) addLL((void **) &CurScript->WWindow->LocalData,
				sizeof(*w) + strlen(Expr));

		if (w)
			{
			strcpy(w->string, Expr);

			(CurScript->WWindow->func)(EVT_DRAWINT, 0, 0, CurScript->WWindow);
			return (TRUE);
			}
		else
			{
			return (FALSE);
			}
		}
	else
		{
		return (FALSE);
		}
	}

static void ToggleWatch(const char *Expr)
	{
	CreateWatchWindow();

	if (CurScript->WWindow)
		{
		strList *w = findWatch(Expr);

		if (w)
			{
			DebuggingMsg(getscrmsg(37));
			RemoveWatch(Expr);
			}
		else
			{
			if (AddWatch(Expr))
				{
				DebuggingMsg(getscrmsg(38));
				}
			else
				{
				DebuggingMsg(getscrmsg(39));
				}
			}
		}
	else
		{
		DebuggingMsg(getscrmsg(40));
		}
	}

Bool dwHandler(EVENT evt, long param, int more, CITWINDOW *wnd)
	{
	switch (evt)
		{
		case EVT_CTRLRET:
			{
			CONTROLINFO *ci = (CONTROLINFO *) param;

			switch (ci->id)
				{
				case CTRL_ABORT:
					{
					CurScript->Error = *((int *)(ci->ptr));
					break;
					}

				case CTRL_QUIT:
					{
					CurScript->Debug = !*((int *)(ci->ptr));
					break;
					}

				case CTRL_EVALEXP:
					{
					char wow[256];

					sprintf(wow, getscrmsg(29), ci->ptr);
					DebuggingMsg(wow);

					if (evaluateString((char *) ci->ptr))
					strcpy(wow, evaluateString((char *) ci->ptr));
					wow[70] = 0;
					DebuggingMsg(wow);

					break;
					}

				case CTRL_BPLINE:
					{
					int bpLine = atoi((char *) ci->ptr);

					if (bpLine > 0)
						{
						Breakpoints *bp;
						int i;

						for (i = 1, bp = CurScript->BPoints; bp;
								i++, bp = (Breakpoints *) getNextLL(bp))
							{
							if (bp->LineNum == bpLine)
								{
								break;
								}
							}

						if (bp)
							{
							DebuggingMsg(getscrmsg(33));
							deleteLLNode((void **) &CurScript->BPoints, i);
							}
						else
							{
							bp = (Breakpoints *) addLL((void **)
									&CurScript->BPoints, sizeof(*bp));

							if (bp)
								{
								DebuggingMsg(getscrmsg(34));
								bp->LineNum = bpLine;
								}
							else
								{
								DebuggingMsg(getscrmsg(35));
								}
							}

						if (CurScript->SourceWindow)
							{
							(CurScript->SourceWindow->func)(EVT_DRAWINT, 0, 0,
									CurScript->SourceWindow);
							}
						}

					break;
					}

				case CTRL_WATCHEXP:
					{
					ToggleWatch((char *) ci->ptr);
					break;
					}
				}

			break;
			}

		case EVT_DRAWINT:
			{
			if (wnd->flags.visible)
				{
				if (buildClipArray(wnd))
					{
					int h = wnd->extents.bottom - wnd->extents.top;
					int w = wnd->extents.right - wnd->extents.left;

					for (int i = 1; i < h; i++)
						{
						if (i >= wnd->extents.bottom - wnd->extents.top)
							{
							break;
							}

						int off = conRows / 2 - h + i - 1;

						if (off < 0)
							{
							off = 0;
							}

						for (int j = 1; j < w; j++)
							{
							CitWindowOutChr(wnd, j, i,
								*(((char *) wnd->LocalData) +
								off * (conCols - 2) + j - 1), cfg.attr);
							}
						}

					for (; i < h; i++)
						{
						CitWindowClearLine(wnd, i, cfg.attr);
						}

					freeClipArray();
					}
				}

			break;
			}

		case EVT_INKEY:
			{
			if (param <= 255)
				{
				if (HandleDebuggingKeyboardCommand((int) param))
					{
					break;
					}
				}

			return (defaultHandler(evt, param, more, wnd));
			}

		default:
			{
			return (defaultHandler(evt, param, more, wnd));
			}
		}

	return (TRUE);
	}
