/*
    CaPriCe Forever - Amstrad CPC 464/664/6128 emulator
    Copyright (C) 2014-2015 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>
#include <Clipbrd.hpp>
#pragma hdrstop

#include "TFormCRTC.h"
#include "TFormEditValue.h"
#include "TFormMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"


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

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


static const AnsiString cTitle = "CRT Controller";
static const AnsiString cTitlePaused  = " - [PAUSED]";


TFormCRTC *FormCRTC;
//---------------------------------------------------------------------------
__fastcall TFormCRTC::TFormCRTC(TComponent* Owner)
  : TForm(Owner)
{
  mSettingsP = NULL;

  for (int i=0; i < CRTC_REGISTERS_NBELEMENT; i++)
  {
    mDisplayValueHexa[i] = true;
  }

  // Init component lists
  for (int Loop=0; Loop < CRTC_REGISTERS_NBELEMENT; Loop++)
  {
    mPanelRegisterFormat[Loop] = (TPanel*)FindComponent("PanelFormatR" + AnsiString(Loop));
    mPanelRegister[Loop] = (TPanel*)FindComponent("PanelR" + AnsiString(Loop));
    mLabelRegister[Loop] = (TLabel*)FindComponent("LabelR" + AnsiString(Loop));
  }

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


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

	FormMain->OnCRTCFormShow();
	FormMain->SubscribeToCPCEvents(OnCPCEvent);

  tNativeCPC* NativeCPC = FormMain->EmulatorP->NativeCPC;
  InitRegisters(NativeCPC);

  mPaused = FormMain->NativeCPC->paused;
  mStepByStep = FormMain->NativeCPC->step_by_step;
  if (mStepByStep == true)
  {
    UpdateStatus(NativeCPC);
    UpdatePosition(NativeCPC);
  }
  else
  {
    ClearStatus();
    ClearPosition();
  }

  Caption = Title;

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

  NOT_USED(Sender);
}
//---------------------------------------------------------------------------
void __fastcall TFormCRTC::FormHide(TObject *Sender)
{
	// Unsubscribe to step by step event
  if (FormMain != NULL)
  {
		FormMain->UnsubscribeToCPCEvents(OnCPCEvent);
		FormMain->OnCRTCFormHide();
	}

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

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


AnsiString __fastcall TFormCRTC::GetTitle(void)
{
  AnsiString Title = cTitle;
  if (mPaused == true) Title += cTitlePaused;
 return Title;
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::SetSettings(TSettings* Value)
{
  mSettingsP = Value;

  if (    (mSettingsP->CRTCFormLeft != 0)
       || (mSettingsP->CRTCFormTop != 0)
       /*|| (mSettingsP->CRTCFormWidth != 0)
       || (mSettingsP->CRTCFormHeight != 0)*/ )
  {
    Position = poDesigned;
    Left = mSettingsP->CRTCFormLeft;
    Top = mSettingsP->CRTCFormTop;
    //Width = mSettings->CRTCFormWidth;
    //Height = mSettings->CRTCFormHeight;
  }

  ShowHint = mSettingsP->ShowHints;

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

		// Refresh display
		if ( (Visible == true) && (((TControl*)Components[Loop])->Visible == true) )
		{
			((TControl*)Components[Loop])->Invalidate();
		}
	}
	PanelCharCount->Font->Color = mSettingsP->ThemeText1Color;
	PanelLineCount->Font->Color = mSettingsP->ThemeText1Color;
	PanelRasterCount->Font->Color = mSettingsP->ThemeText1Color;
	PanelRasterCount->Font->Color = mSettingsP->ThemeText1Color;
	PanelBeamPosX->Font->Color = mSettingsP->ThemeText1Color;
	PanelBeamPosY->Font->Color = mSettingsP->ThemeText1Color;

	mLabelRegister[mLastDisplayedRegisterSelected]->Font->Color = mSettingsP->ThemeText2Color;

	// Set hexa format
	for (int Index=0; Index < MAX_CRTC_REGISTERS; Index++)
	{
		mDisplayValueHexa[Index] = mSettingsP->GetCRTCRegHexaFormat(Index);

		if (mDisplayValueHexa[Index])
		{
			mPanelRegisterFormat[Index]->Caption = "&&";
		}
		else
		{
			mPanelRegisterFormat[Index]->Caption = "";
		}

		UpdateRegister(Index);
	}

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


void __fastcall TFormCRTC::FormResize(TObject *Sender)
{
	if ( (Visible == true) && (mSettingsP) )
	{
		mSettingsP->CRTCFormWidth = Width;
		mSettingsP->CRTCFormHeight = Height;
	}

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

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


void __fastcall TFormCRTC::InitRegisters(tNativeCPC* NativeCPC)
{
  // Initialiser registers value
  for (int Index=0; Index < CRTC_REGISTERS_NBELEMENT; Index++)
  {
    mPreviousRegistersP[Index] = NativeCPC->CRTC->registers[Index];
    mLastDisplayedRegistersP[Index] = NativeCPC->CRTC->registers[Index];

    if (mDisplayValueHexa[Index])
    {
      mPanelRegister[Index]->Caption = AnsiString::IntToHex(mLastDisplayedRegistersP[Index], 2);
    }
    else
    {
      mPanelRegister[Index]->Caption = mLastDisplayedRegistersP[Index];
    }
  }
  
  mLabelRegister[mLastDisplayedRegisterSelected]->Font->Color = mSettingsP->ThemeText1Color;
  mLastDisplayedRegisterSelected = NativeCPC->CRTC->reg_select;
  mLabelRegister[mLastDisplayedRegisterSelected]->Font->Color = mSettingsP->ThemeText2Color;
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::UpdateRegisters(tNativeCPC* NativeCPC)
{
  // Initialiser registers value
  for (int Index=0; Index < CRTC_REGISTERS_NBELEMENT; Index++)
  {
    if (mLastDisplayedRegistersP[Index] != NativeCPC->CRTC->registers[Index])
    {
      mLastDisplayedRegistersP[Index] = NativeCPC->CRTC->registers[Index];

      if (mDisplayValueHexa[Index])
      {
        mPanelRegister[Index]->Caption = AnsiString::IntToHex(mLastDisplayedRegistersP[Index], 2);
      }
      else
      {
        mPanelRegister[Index]->Caption = mLastDisplayedRegistersP[Index];
      }
    }
  }

  if (NativeCPC->CRTC->reg_select < 18)
  {
    if (mLastDisplayedRegisterSelected != NativeCPC->CRTC->reg_select)
    {
      mLabelRegister[mLastDisplayedRegisterSelected]->Font->Color = mSettingsP->ThemeText1Color;
      mLastDisplayedRegisterSelected = NativeCPC->CRTC->reg_select;
      mLabelRegister[mLastDisplayedRegisterSelected]->Font->Color = mSettingsP->ThemeText2Color;
    }
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::UpdateStatus(tNativeCPC* NativeCPC)
{
  CheckBoxVBL->Checked = Engine_IsVideoVBL(NativeCPC);
  CheckBoxHBL->Checked = Engine_IsVideoHBL(NativeCPC);

  // Display Char Count
  AnsiString Message = "&&" + AnsiString::IntToHex(Engine_GetVideoCharCount(NativeCPC), 2);
  Message += " / &&" + AnsiString::IntToHex(NativeCPC->CRTC->registers[0], 2);
  PanelCharCount->Caption = Message;

  // Display Line Count
  Message = "&&" + AnsiString::IntToHex(Engine_GetVideoLineCount(NativeCPC), 2);
  Message += " / &&" + AnsiString::IntToHex(NativeCPC->CRTC->registers[4], 2);
  PanelLineCount->Caption = Message;

  // Display Raster Count
  Message = "&&" + AnsiString::IntToHex(Engine_GetVideoRasterCount(NativeCPC), 2);
  Message += " / &&" + AnsiString::IntToHex(NativeCPC->CRTC->registers[9], 2);
  PanelRasterCount->Caption = Message;
}
//---------------------------------------------------------------------------

void __fastcall TFormCRTC::ClearStatus(void)
{
  CheckBoxVBL->Checked = false;
  CheckBoxHBL->Checked = false;
  PanelCharCount->Caption = "";
  PanelLineCount->Caption = "";
  PanelRasterCount->Caption = "";
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::UpdatePosition(tNativeCPC* NativeCPC)
{
  // Display Scanline
  PanelBeamPosX->Caption = Engine_GetVideoBeamPosX(NativeCPC);
  PanelBeamPosY->Caption = Engine_GetVideoBeamPosY(NativeCPC);
}
//---------------------------------------------------------------------------

void __fastcall TFormCRTC::ClearPosition(void)
{
  PanelBeamPosX->Caption = "";
  PanelBeamPosY->Caption = "";
}
//---------------------------------------------------------------------------


bool __fastcall TFormCRTC::OnCPCEvent(tULong Condition, tNativeCPC* NativeCPC)
{
  UpdateRegisters(NativeCPC);

  bool Pause = NativeCPC->paused;
  bool StepByStep = NativeCPC->step_by_step;

  if ( (mStepByStep == true) && (mStepByStep != StepByStep) )
  {
    mStepByStep = false;
    Caption = Title;

    if (Pause == false)
    {
      // Clear display
      ClearStatus();
      ClearPosition();
    }
  }
  else if (Pause != mPaused)
  {
    if (Pause == true)
    {
      UpdateStatus(NativeCPC);
      UpdatePosition(NativeCPC);
    }
    else
    {
      ClearStatus();
      ClearPosition();
    }

    mPaused = Pause;
    Caption = Title;
  }

  if ( ((Condition & EC_END_INSTRUCTION) == 0) && (Pause == false) )
    return false;

  // Register update
  if (StepByStep == true)
  {
    mStepByStep = true;

    UpdateStatus(NativeCPC);
    UpdatePosition(NativeCPC);
  }

  return Pause;
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::UpdateRegister(int Index)
{
  if (mDisplayValueHexa[Index])
  {
    mPanelRegister[Index]->Caption = AnsiString::IntToHex(mLastDisplayedRegistersP[Index], 2);
  }
  else
  {
    mPanelRegister[Index]->Caption = mLastDisplayedRegistersP[Index];
  }
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::UpdateRegisterFormat(int Index)
{
  mDisplayValueHexa[Index] ^= 1; // Invert

  if (mDisplayValueHexa[Index])
  {
    mPanelRegisterFormat[Index]->Caption = "&&";
  }
  else
  {
    mPanelRegisterFormat[Index]->Caption = "";
  }
  
  UpdateRegister(Index);
  mSettingsP->SetCRTCRegHexaFormat(Index, mDisplayValueHexa[Index]);
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::PanelFormatClick(TObject *Sender)
{
  UpdateRegisterFormat(((TPanel*)Sender)->Tag);
}
//---------------------------------------------------------------------------


void __fastcall TFormCRTC::PanelRegClick(TObject *Sender)
{
  int Index = ((TPanel*)Sender)->Tag;

  TFormEditValue* EditValue = new TFormEditValue(this);
  EditValue->Settings = mSettingsP;
  EditValue->CurrentValue = FormMain->EmulatorP->NativeCPC->CRTC->registers[Index];
  EditValue->Hexadecimal = mDisplayValueHexa[Index];
  EditValue->MinValue = 0;
  EditValue->MaxValue = 255;
  if (EditValue->ShowModal() == mrOk)
  {
    int NewValue = EditValue->NewValue;
    tNativeCPC* NativeCPC = FormMain->EmulatorP->NativeCPC;
    
    if (NativeCPC->SetCRTCRegisterPtr)
    {
      NativeCPC->SetCRTCRegisterPtr(NativeCPC, Index, NewValue);
    }
    else
    {
      NativeCPC->CRTC->registers[Index] = NewValue;
    }
    NativeCPC->CRTC->signal_register_update = 1;
    mLastDisplayedRegistersP[Index] = NewValue;
    UpdateRegister(Index);
  }
  delete EditValue;
}
//---------------------------------------------------------------------------


