 // ###########################################################################
// #
// # FormDiag - Diagnostic Messages Display Form
// # Copyright (C) 2002-2009 Tony Friery [DialTone]
// #
// # ALL RIGHTS RESERVED
// #
// ###########################################################################

#include <vcl.h>
#include <vcl\Clipbrd.hpp>

#pragma hdrstop

#include "FormDiag.h"

#pragma package(smart_init)
#pragma resource "*.dfm"

///////////////////////////////////////////////////////////////////////
//
//		String Representations of Diagnostic Message Types
//
///////////////////////////////////////////////////////////////////////

const wchar_t *Msg_Types[] =
{
	L"CPU",
	L"PIC",
	L"TIMER",
	L"AUDIO",
	L"OSCA Misc",
	L"PALETTE",
	L"VIDEO",
	L"KB/MOUSE",
	L"MATHS",
	L"SD/MMC",
	L"RS232",
	L"UNUSED"
};

TDiagForm *DiagForm;

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::BuildCopiedData
//
//  PURPOSE:	Dump all diagnostic messages into a TMemoryStream in
//					preparation for export to a file or to the clipboard
//
//  INPUTS:		none
//
//  OUTPUT:		none
//
//  NOTES:		Leaves fCopiedDataStream initialised and containing
//					the data, which is null-terminated
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::BuildCopiedData(void)
{
	TListItem *li;
	wchar_t *item;

	// Construct TMemoryStream Object
	fCopiedDataStream = new TMemoryStream();

	// Check if there are diagnostic messages to process
	if (ListMessages->Items->Count)
	{
		// There are, so iterate through them
		for (int i = 0; i < ListMessages->Items->Count; i++)
		{
			// Retrieve diagnostic message item (identifier)
			li = ListMessages->Items->Item[i];
			item = li->Caption.w_str();

			// Write item to the TMemoryStream followed by a tab char
			fCopiedDataStream->Write(AnsiString(item).c_str(), AnsiString(item).Length());
			fCopiedDataStream->Write("\t", 1);

			// Retrieve diagnostic message item (message)
			item = li->SubItems->Strings[0].w_str();

			// Write item to the TMemoryStream followed by CR + LF
			fCopiedDataStream->Write(AnsiString(item).c_str(), AnsiString(item).Length());
			fCopiedDataStream->Write("\x0d\x0a", 2);
		}
	}

	// Finally, append a NULL terminator to the data in the
	// TMemoryStream, even if no data was written into it
	fCopiedDataStream->Write("\x00", 1);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::DeleteCopiedData
//
//  PURPOSE:	Dispose of the TMemoryStream which was previously
//					instantiated by BuildCopiedData() call
//
//  INPUTS:		none
//
//  OUTPUT:		none
//
//  NOTES:		Destroys fCopiedDataStream and NULLS the pointer
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::DeleteCopiedData(void)
{
	if (fCopiedDataStream)
	{
		delete fCopiedDataStream;
		fCopiedDataStream = NULL;
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::AddMessage
//
//  PURPOSE:	Append a diagnostic message to the message list,
//					provided that the filter settings permit this type
//					of message
//
//  INPUTS:		DiagMsgTypes		Type Identifier for the Message
//					UnicodeString		String containing the Message
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::AddMessage(DiagMsgTypes MsgType, UnicodeString Message)
{
	if ((fLogStream) && ((ChkLogToFile->Checked) || (fMsgTypeActive[(int)MsgType])))
	{
		fLogStream->Write(AnsiString(Msg_Types[(int)MsgType]).c_str(),
			AnsiString(Msg_Types[(int)MsgType]).Length());
		fLogStream->Write("\t", 1);

		// Write item to the stream followed by CR + LF
		fLogStream->Write(AnsiString(Message).c_str(), AnsiString(Message).Length());
		fLogStream->Write("\x0d\x0a", 2);
	}

	// Check if the filter for this message type permits inclusion
	if (fMsgTypeActive[(int)MsgType])
	{
		// It does, so add this item
		if (ListMessages->Items->Count == 0x8000)
		{
			// Prevent Overflowing
			ListMessages->Items->Delete(0);
		}

		TListItem *li = ListMessages->Items->Add();
		li->Caption = UnicodeString(Msg_Types[(int)MsgType]);
		li->SubItems->Add(Message);
		li->MakeVisible(false);
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::BtnCloseClick
//
//  PURPOSE:	Event triggered by the BtnClose button requesting
//					that the Diagnostic window be closed
//
//  INPUTS:		TObject *			Pointer to the BtnClose Object
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::BtnCloseClick(TObject *)
{
	this->Close();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::BtnClearClick
//
//  PURPOSE:	Event triggered by the BtnClear button requesting
//					that the Diagnostic Message List be cleared
//
//  INPUTS:		TObject *			Pointer to the BtnClear Object
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::BtnClearClick(TObject *)
{
	ListMessages->Items->Clear();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::BtnCopyClick
//
//  PURPOSE:	Event triggered by the BtnCopy button requesting
//					that the Diagnostic Messages be copied onto the
//					clipboard
//
//  INPUTS:		TObject *			Pointer to the BtnCopy Object
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::BtnCopyClick(TObject *)
{
	// Obtain a reference to the TClipboard Object
	TClipboard *clip = Clipboard();

	// Prepare Memory Stream
	BuildCopiedData();

	// Transfer Data from Memory Stream to Clipboard
	clip->Open();
	clip->SetTextBuf(UnicodeString((char *)fCopiedDataStream->Memory).w_str());
	clip->Close();

	// Destroy Memory Stream
	DeleteCopiedData();
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::BtnExportClick
//
//  PURPOSE:	Event triggered by the BtnExport button requesting
//					that the Diagnostic Messages be saved to a file
//
//  INPUTS:		TObject *			Pointer to the BtnExport Object
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::BtnExportClick(TObject *)
{
	// Display dialog and check it wasn't cancelled
	if (SaveDialog->Execute())
	{
		// Dialog was not cancelled, so prepare the Memory Stream
		BuildCopiedData();

		// Write Memory Stream contents to specified filename
		fCopiedDataStream->SaveToFile(SaveDialog->FileName);

		// Destroy the Memory Stream as it's now finished with
		DeleteCopiedData();
	}
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::ChkTypeModified
//
//  PURPOSE:	Event triggered by a change in a TCheckBox items which
//					is used for flagging individual Message Types
//
//  INPUTS:		TObject *			Pointer to the TCheckBox Object
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
void __fastcall TDiagForm::ChkTypeModified(TObject *Sender)
{
	// Update Type Selected Array
	fMsgTypeActive[((TCheckBox *)(Sender))->Tag] =
		((TCheckBox *)(Sender))->Checked;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::IsActive
//
//  PURPOSE:	Returns whether a particular message type is enabled.
//					(Used in order to reduce the unnecessary overhead of
//					formatting debug messages when they're not being
//					dislayed)
//
//  INPUTS:		DiagMsgTypes		Type Identifier for the Message
//
//  OUTPUT:		bool					true if msg type is enabled, or if
//											log all to file enabled, otherwise
//											false
//
///////////////////////////////////////////////////////////////////////
bool __fastcall TDiagForm::IsActive(DiagMsgTypes MsgType)
{
	if ((fMsgTypeActive[MsgType]) || (ChkLogToFile->Checked))
		return true;

	return false;
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::TDiagForm [CONSTRUCTOR]
//
//  PURPOSE:	Constructor for the Diagnostic Window
//
//  INPUTS:		TComponent *		Pointer to the Object owning this window
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
__fastcall TDiagForm::TDiagForm(TComponent* Owner)
	: TForm(Owner)
{
	TCheckBox *Filters[] =
	{
		DiagForm->ChkCPU, DiagForm->ChkPIC, DiagForm->ChkTimer,
		DiagForm->ChkAudio, DiagForm->ChkOSCAMisc, DiagForm->ChkPalette,
		DiagForm->ChkVideo, DiagForm->ChkKBMouse, DiagForm->ChkMathsUnit,
		DiagForm->ChkSDMMC, DiagForm->ChkRS232, DiagForm->ChkUnused8
	};

	// Copy initial Selected values from checkboxes
	for (int i = 0; i < 12; i++)
	{
		fMsgTypeActive[i] = Filters[i]->Checked;
	}

	ListMessages->Items->Clear();
	fLogStream = new TFileStream(L"oscaemu.log", fmCreate);
}

///////////////////////////////////////////////////////////////////////
//
//  FUNCTION:	TDiagForm::~TDiagForm [DESTRUCTOR]
//
//  PURPOSE:	Destructor for the Diagnostic Window
//
//  INPUTS:		none
//
//  OUTPUT:		none
//
///////////////////////////////////////////////////////////////////////
__fastcall TDiagForm::~TDiagForm()
{
	if (fLogStream)
	{
		delete fLogStream;
		fLogStream = NULL;
	}
}

//---------------------------------------------------------------------------

