/*
    CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
    Copyright (C) 2014-2020 by Frédéric Coste

    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 3 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, see <http://www.gnu.org/licenses/>.
*/


#include <vcl.h>
#pragma hdrstop
#include "TFormActionReplay.h"
#include "TFormMain.h"
#include "TFormMemoryEditor.h"
#include "TFormDebugger.h"


//=================================
// Options

//=================================


//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormActionReplay *FormActionReplay;
//---------------------------------------------------------------------------
__fastcall TFormActionReplay::TFormActionReplay(TComponent* Owner)
	: TForm(Owner)
{
  mSettingsP = NULL;

	mMemorySize = 0x10000;
	mMatchFilteringCount = 0;
	mUseBank0 = true;
	mUseBank1 = true;
	mUseBank2 = true;
	mUseBank3 = true;
	mUseFixedValue = false;
	mValue = 0;

	mBeforePreviousMemory = new tUChar[mMemorySize];
	mPreviousMemory = new tUChar[mMemorySize];
	mMatchFiltering = new tUChar[mMemorySize];

	mFormEditValue = new TFormEditValue(this);

	GroupBoxMatchResult->Caption = " " + AnsiString(mMatchFilteringCount) + " result(s) ";
	PanelEditValue->Caption = AnsiString::IntToHex(mValue, 2);

	// Apply theme
	Font->Name = "Amstrad CPC464 Regular";
	Font->Size = 6;
	for (int Loop=0; Loop < ComponentCount; Loop++)
	{
		if (Components[Loop]->Name.Pos("PanelFormat") == 1)
		{
			((TPanel*)Components[Loop])->Font->Color = clBlack;
		}
		else if (Components[Loop]->Name.Pos("PanelEdit") == 1)
		{
			((TPanel*)Components[Loop])->Font->Color = clBlack;
		}
	}

	StringGridResults->Visible = false;
	StringGridResults->DefaultDrawing = false;
	StringGridResults->Canvas->Font = Font;
	StringGridResults->ColCount = 2;
	StringGridResults->ColWidths[0] = 50;
	StringGridResults->ColWidths[1] = StringGridResults->Width - StringGridResults->ColWidths[0];
	PanelButtonAddBreakpoint->Visible = false;
	PanelButtonShowMemory->Visible = false;
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::FormDestroy(TObject *Sender)
{
	delete [] mBeforePreviousMemory;
	delete [] mPreviousMemory;
	delete [] mMatchFiltering;

	delete mFormEditValue;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::FormShow(TObject *Sender)
{
	if (FormMain == NULL)
	{
		Close();
		return;
	}

	FormMain->OnActionReplayFormShow();

	if (mSettingsP)
	{
		mSettingsP->ActionReplayFormVisible = true;
	}

	// Reset filtering
	PanelButtonResetClick(Sender);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::FormHide(TObject *Sender)
{
	if (FormMain != NULL)
	{
		FormMain->OnActionReplayFormHide();
	}

	if (mSettingsP)
	{
		mSettingsP->ActionReplayFormVisible = false;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::SetSettings(TSettings* Value)
{
	mSettingsP = Value;
	mFormEditValue->Settings = mSettingsP;

	if (    (mSettingsP->ActionReplayFormLeft != 0)
			 || (mSettingsP->ActionReplayFormTop != 0)
			 || (mSettingsP->ActionReplayFormWidth != 0)
			 || (mSettingsP->ActionReplayFormHeight != 0) )
	{
		Position = poDesigned;
		Left = mSettingsP->ActionReplayFormLeft;
		Top = mSettingsP->ActionReplayFormTop;
		Width = mSettingsP->ActionReplayFormWidth;
    Height = mSettingsP->ActionReplayFormHeight;
  }

  ShowHint = mSettingsP->ShowHints;

	// Apply theme
	Color = mSettingsP->ThemeBackgroundColor;
	Font->Color = mSettingsP->ThemeText1Color;
	for (int Loop=0; Loop < ComponentCount; Loop++)
	{
		if (Components[Loop]->Name.Pos("PanelButton") == 1)
		{
			((TPanel*)Components[Loop])->Color = mSettingsP->ThemeButtonColor;
			((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeButtonTextColor;
		}
		else if (Components[Loop]->Name.Pos("GroupBox") == 1)
		{
			((TGroupBox*)Components[Loop])->Font->Color = mSettingsP->ThemeText2Color;
		}
		else if (Components[Loop]->Name.Pos("PanelCheck") == 1)
		{
			((TPanel*)Components[Loop])->Color = mSettingsP->ThemeButtonColor;
			((TPanel*)Components[Loop])->Font->Color = mSettingsP->ThemeButtonTextColor;
		}

		// Refresh display
		if ( (Visible == true) && (((TControl*)Components[Loop])->Visible == true) )
		{
			((TControl*)Components[Loop])->Invalidate();
		}
	}


	if (mUseBank0 == true)
	{
		PanelCheckBank0->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank0->Font->Color = mSettingsP->ThemeButtonColor;
	}
	if (mUseBank1 == true)
	{
		PanelCheckBank1->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank1->Font->Color = mSettingsP->ThemeButtonColor;
	}
	if (mUseBank2 == true)
	{
		PanelCheckBank2->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank2->Font->Color = mSettingsP->ThemeButtonColor;
	}
	if (mUseBank3 == true)
	{
		PanelCheckBank3->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank3->Font->Color = mSettingsP->ThemeButtonColor;
	}
	if (mUseFixedValue == true)
	{
		PanelCheckFixed->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckFixed->Font->Color = mSettingsP->ThemeButtonColor;
	}
	else
	{
		PanelCheckLast->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckLast->Font->Color = mSettingsP->ThemeButtonColor;
	}

	if (mSettingsP->HideAll == false)
  {
    Visible = mSettingsP->ActionReplayFormVisible;
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::FormResize(TObject *Sender)
{
	if ( (Visible == true) && (mSettingsP) )
	{
		mSettingsP->ActionReplayFormWidth = Width;
		mSettingsP->ActionReplayFormHeight = Height;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TFormActionReplay::WMMove(TMessage &Message)
{
	if ( (Visible == true) && (mSettingsP) )
  {
		mSettingsP->ActionReplayFormLeft = Left;
    mSettingsP->ActionReplayFormTop = Top;
  }

	NOT_USED(Message);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelButtonMouseDown(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y)
{
  ((TPanel*)Sender)->Color = mSettingsP->ThemeButtonTextColor;
  ((TPanel*)Sender)->Font->Color = mSettingsP->ThemeButtonColor;

  NOT_USED(Button);
  NOT_USED(Shift);
  NOT_USED(X);
  NOT_USED(Y);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelButtonMouseUp(TObject *Sender, TMouseButton Button,
          TShiftState Shift, int X, int Y)
{
  ((TPanel*)Sender)->Color = mSettingsP->ThemeButtonColor;
  ((TPanel*)Sender)->Font->Color = mSettingsP->ThemeButtonTextColor;

  NOT_USED(Button);
  NOT_USED(Shift);
  NOT_USED(X);
  NOT_USED(Y);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelButtonResetClick(TObject *Sender)
{
	for (tULong Index=0; Index < mMemorySize; Index++)
	{
		mPreviousMemory[Index] = (tUChar)Engine_ReadBankedWrite(FormMain->NativeCPC, Index);
		mBeforePreviousMemory[Index] = mPreviousMemory[Index];
		mMatchFiltering[Index] = 1;
	}

	if (mUseBank0 == false)
	{
		for (tULong Index=0x0000; Index < 0x4000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}
	if (mUseBank1 == false)
	{
		for (tULong Index=0x4000; Index < 0x8000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}
	if (mUseBank2 == false)
	{
		for (tULong Index=0x8000; Index < 0xC000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}
	if (mUseBank3 == false)
	{
		for (tULong Index=0xC000; Index < 0x10000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}

	UpdateMatchingResult();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelButtonFilterEqualClick(TObject *Sender)
{
	for (tULong Index=0; Index < mMemorySize; Index++)
	{
		// Still matching ?
		if (mMatchFiltering[Index])
		{
			tUChar CurrentData = (tUChar)Engine_ReadBankedWrite(FormMain->NativeCPC, Index);

			if ( (mUseFixedValue == false) && (CurrentData == mPreviousMemory[Index]) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else if ( (mUseFixedValue == true) && (CurrentData == mValue) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else
			{
				mMatchFiltering[Index] = 0;
			}
		}
	}

	UpdateMatchingResult();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelButtonFilterDiffClick(TObject *Sender)
{
	for (tULong Index=0; Index < mMemorySize; Index++)
	{
		// Still matching ?
		if (mMatchFiltering[Index])
		{
			tUChar CurrentData = (tUChar)Engine_ReadBankedWrite(FormMain->NativeCPC, Index);

			if ( (mUseFixedValue == false) && (CurrentData != mPreviousMemory[Index]) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else if ( (mUseFixedValue == true) && (CurrentData != mValue) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else
			{
				mMatchFiltering[Index] = 0;
			}
		}
	}

	UpdateMatchingResult();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelButtonFilterLessClick(TObject *Sender)
{
	for (tULong Index=0; Index < mMemorySize; Index++)
	{
		// Still matching ?
		if (mMatchFiltering[Index])
		{
			tUChar CurrentData = (tUChar)Engine_ReadBankedWrite(FormMain->NativeCPC, Index);

			if ( (mUseFixedValue == false) && (CurrentData < mPreviousMemory[Index]) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else if ( (mUseFixedValue == true) && (CurrentData < mValue) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else
			{
				mMatchFiltering[Index] = 0;
			}
		}
	}

	UpdateMatchingResult();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelButtonFilterMoreClick(TObject *Sender)
{
	for (tULong Index=0; Index < mMemorySize; Index++)
	{
		// Still matching ?
		if (mMatchFiltering[Index])
		{
			tUChar CurrentData = (tUChar)Engine_ReadBankedWrite(FormMain->NativeCPC, Index);

			if ( (mUseFixedValue == false) && (CurrentData > mPreviousMemory[Index]) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else if ( (mUseFixedValue == true) && (CurrentData > mValue) )
			{
				mBeforePreviousMemory[Index] = mPreviousMemory[Index];
				mPreviousMemory[Index] = CurrentData;
			}
			else
			{
				mMatchFiltering[Index] = 0;
			}
		}
	}

	UpdateMatchingResult();

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::UpdateMatchingResult(void)
{
	mMatchFilteringCount = 0;

	for (tULong Index=0; Index < mMemorySize; Index++)
	{
		// Still matching ?
		if (mMatchFiltering[Index])
		{
			mMatchFilteringCount++;
		}
	}

	GroupBoxMatchResult->Caption = " " + AnsiString(mMatchFilteringCount) + " result(s) ";

	StringGridResults->Visible = false;
	PanelButtonAddBreakpoint->Visible = false;
	PanelButtonShowMemory->Visible = false;
	// Automatic show ?
	if (mMatchFilteringCount)
	{
		if (mMatchFilteringCount <= (tULong)(StringGridResults->Height / StringGridResults->DefaultRowHeight))
		{
			PanelButtonShowResultsClick(NULL);
		}
	}
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelButtonShowResultsClick(TObject *Sender)
{
	if (!mMatchFilteringCount)
	{
		StringGridResults->Visible = false;
		PanelButtonAddBreakpoint->Visible = false;
		PanelButtonShowMemory->Visible = false;
	}
	else
	{
		StringGridResults->RowCount = mMatchFilteringCount;
		StringGridResults->Visible = true;
		PanelButtonAddBreakpoint->Visible = true;
		PanelButtonShowMemory->Visible = true;

		int RowIndex = 0;
		for (tULong Index=0; Index < mMemorySize; Index++)
		{
			// Still match filtering ?
			if (mMatchFiltering[Index])
			{
				tUChar FromValue = mBeforePreviousMemory[Index];
				tUChar ToValue = mPreviousMemory[Index];
				StringGridResults->Cells[0][RowIndex] = AnsiString::IntToHex(Index, 4) + ":";
				if (FromValue != ToValue)
				{
					StringGridResults->Cells[1][RowIndex] = AnsiString::IntToHex(FromValue, 2)
											+ " -> " + AnsiString::IntToHex(ToValue, 2);
				}
				else
				{
					StringGridResults->Cells[1][RowIndex] = AnsiString::IntToHex(FromValue, 2);
				}
				RowIndex++;
			}
		}
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::StringGridResultsDrawCell(TObject *Sender, int ACol,
					int ARow, TRect &Rect, TGridDrawState State)
{
	TStringGrid* StringGrid = (TStringGrid*)Sender;

	if ( (State.Contains(gdSelected)) && (StringGrid->Enabled == true) )
	{
		StringGrid->Canvas->Brush->Color = clHighlight;
		StringGrid->Canvas->Font->Color = clWhite;
	}
	else if (ACol == 0)
	{
		StringGrid->Canvas->Brush->Color = Color;
		StringGrid->Canvas->Font->Color = mSettingsP->ThemeText2Color;
	}
	else
	{
		StringGrid->Canvas->Brush->Color = Color;
		StringGrid->Canvas->Font->Color = mSettingsP->ThemeText1Color;
	}

  // Draw cell
	StringGrid->Canvas->FillRect(Rect);
	AnsiString Content = StringGrid->Cells[ACol][ARow];
	if (Content.Length() > 0)
	{
		int TextHeight = StringGrid->Canvas->TextHeight(Content);
		int TextPos = (StringGrid->RowHeights[ARow] - TextHeight) / 2;
		StringGrid->Canvas->TextOut(Rect.Left+5,
															Rect.Top+TextPos,
															Content);
	}

	NOT_USED(ARow);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelCheckBank0Click(TObject *Sender)
{
	mUseBank0 = !mUseBank0;

	if (mUseBank0 == false)
	{
		for (tULong Index=0x0000; Index < 0x4000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}

	UpdateMatchingResult();

	if (mUseBank0 == true)
	{
		PanelCheckBank0->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank0->Font->Color = mSettingsP->ThemeButtonColor;
	}
	else
	{
		PanelCheckBank0->Color = mSettingsP->ThemeButtonColor;
		PanelCheckBank0->Font->Color = mSettingsP->ThemeButtonTextColor;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelCheckBank1Click(TObject *Sender)
{
	mUseBank1 = !mUseBank1;

	if (mUseBank1 == false)
	{
		for (tULong Index=0x4000; Index < 0x8000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}

	UpdateMatchingResult();

	if (mUseBank1 == true)
	{
		PanelCheckBank1->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank1->Font->Color = mSettingsP->ThemeButtonColor;
	}
	else
	{
		PanelCheckBank1->Color = mSettingsP->ThemeButtonColor;
		PanelCheckBank1->Font->Color = mSettingsP->ThemeButtonTextColor;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelCheckBank2Click(TObject *Sender)
{
	mUseBank2 = !mUseBank2;

	if (mUseBank2 == false)
	{
		for (tULong Index=0x8000; Index < 0xC000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}

	UpdateMatchingResult();

	if (mUseBank2 == true)
	{
		PanelCheckBank2->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank2->Font->Color = mSettingsP->ThemeButtonColor;
	}
	else
	{
		PanelCheckBank2->Color = mSettingsP->ThemeButtonColor;
		PanelCheckBank2->Font->Color = mSettingsP->ThemeButtonTextColor;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelCheckBank3Click(TObject *Sender)
{
	mUseBank3 = !mUseBank3;

	if (mUseBank3 == false)
	{
		for (tULong Index=0xC000; Index < 0x10000; Index++)
		{
			mMatchFiltering[Index] = 0;
		}
	}

	UpdateMatchingResult();

	if (mUseBank3 == true)
	{
		PanelCheckBank3->Color = mSettingsP->ThemeButtonTextColor;
		PanelCheckBank3->Font->Color = mSettingsP->ThemeButtonColor;
	}
	else
	{
		PanelCheckBank3->Color = mSettingsP->ThemeButtonColor;
		PanelCheckBank3->Font->Color = mSettingsP->ThemeButtonTextColor;
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelFormatValueClick(TObject *Sender)
{
	if (PanelFormatValue->Caption.IsEmpty() == true)
  {
		PanelFormatValue->Caption = "&&";
		PanelEditValue->Caption = AnsiString::IntToHex(mValue, 2);
	}
	else
	{
		PanelFormatValue->Caption = "";
		PanelEditValue->Caption = mValue;
  }

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelCheckLastClick(TObject *Sender)
{
	mUseFixedValue = false;

	PanelCheckFixed->Color = mSettingsP->ThemeButtonColor;
	PanelCheckFixed->Font->Color = mSettingsP->ThemeButtonTextColor;
	PanelCheckLast->Color = mSettingsP->ThemeButtonTextColor;
	PanelCheckLast->Font->Color = mSettingsP->ThemeButtonColor;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelCheckFixedClick(TObject *Sender)
{
	mUseFixedValue = true;

	PanelCheckFixed->Color = mSettingsP->ThemeButtonTextColor;
	PanelCheckFixed->Font->Color = mSettingsP->ThemeButtonColor;
	PanelCheckLast->Color = mSettingsP->ThemeButtonColor;
	PanelCheckLast->Font->Color = mSettingsP->ThemeButtonTextColor;

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

void __fastcall TFormActionReplay::PanelEditValueClick(TObject *Sender)
{
  mFormEditValue->Title = "Fixed value";
	mFormEditValue->CurrentValue = mValue;
  mFormEditValue->Hexadecimal = PanelFormatValue->Caption.IsEmpty() == true ? false : true;
  mFormEditValue->MinValue = 0;
  mFormEditValue->MaxValue = 255;
	if (mFormEditValue->ShowModal() == mrOk)
	{
		mValue = (tUChar)mFormEditValue->NewValue;
		PanelEditValue->Caption = (PanelFormatValue->Caption.IsEmpty() == false) ?
															AnsiString::IntToHex(mValue, 2) :
															AnsiString(mValue);

		PanelCheckFixedClick(Sender);
	}

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelButtonAddBreakpointClick(TObject *Sender)
{
	AnsiString AddressString = StringGridResults->Cells[0][StringGridResults->Row];
	// Remove trailing ":"
	AddressString = AddressString.SubString(0, AddressString.Pos(":") - 1);

	TEmulatorBreakpoint Breakpoint;
	Breakpoint.Address = StrToInt("0x" + AddressString);
	Breakpoint.MemoryRead = false;
	Breakpoint.MemoryWrite = true;
	Breakpoint.LowerROM = false;
	Breakpoint.UpperROM = false;
	FormDebugger->ToggleBreakpointMem(Breakpoint);

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------


void __fastcall TFormActionReplay::PanelButtonShowMemoryClick(TObject *Sender)
{
	AnsiString AddressString = StringGridResults->Cells[0][StringGridResults->Row];
	// Remove trailing ":"
	AddressString = AddressString.SubString(0, AddressString.Pos(":") - 1);

	FormMemoryEditor->ShowCurrentAddress(StrToInt("0x" + AddressString));

	NOT_USED(Sender);
}
//---------------------------------------------------------------------------

