//---------------------------------------------------------------------------
/*
Remote Train Control Program for Windows

© Copyright 2019 by Mark DiVecchio

    This file is part of Remote Train Control.

    Remote Train Control 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.

    Remote Train Control 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 Remote Train Control.  If not, see <http://www.gnu.org/licenses/>.

*/
//---------------------------------------------------------------------------
#include <vcl.h>
#include <tchar.h>
#pragma hdrstop

#include "Train_Control.h"
#include "Comm_Thread.h"
#include "Debug_Controls.h"
#include "Setup_Control.h"
#include "Clipbrd.hpp"

#include <stdio.h>
#include <time.h>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "HideButton"
#pragma link "AbNumEdit"
#pragma resource "*.dfm"

TDB *DB;

extern TCommThread * ptrTIUCommThread;				// pointer to TIU communications thread
extern int PC_Tenths;								// RunTimeinTenths counter (system wide)
extern int TimeStampMode;                           // Debug messages time stamping

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

__fastcall TDB::TDB(TComponent* Owner)
    : TForm(Owner)
{
MainForm->bDebug = false;	// turn off debugging until form is shown.
//https://stackoverflow.com/questions/6049053/increase-size-limit-of-trichedit
// expand the size of RichEdit control and setup visibility and alignment.
Debug_Messages->MaxLength = System::MaxInt-2;
Debug_Messages->Clear();
Debug_Messages->Visible = true;
Debug_Messages->Align = alClient;

// Experimental - use Drop Down Menu style in CB10.1
#if __BORLANDC__ >= 0x720
enum DECLSPEC_DENUM TButtonStyle : unsigned char { bsPushButton, bsCommandLink, bsSplitButton };
//Prog_Control_Button->Style = Prog_Control_Button->Style + 2;    // bsSplitButton defined in Vcl.StdCtrls.hpp is really not defined
ClearDebugButton->Style = (TCustomButton::TButtonStyle)bsSplitButton;
ClearDebugButton->DropDownMenu = DebugPopupX;
#endif

TopLeftPos = Point(DB->Left, DB->Top); // save for position of Find dialog
}
//---------------------------------------------------------------------------

__fastcall TDB::~TDB(void)
{
FindDialog1->CloseDialog();
if (MainForm->bSaveRegistryInfo) {
    // write Preferences from registry if they exist
    TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
    assert(RegFile != NULL);
    __try {
		RegFile->WriteInteger("Debug", "Top", Top > 0 ? Top : 0);
		RegFile->WriteInteger("Debug", "Height", Height);
        RegFile->WriteInteger("Debug", "Left", Left > 0 ? Left : 0);
        RegFile->WriteInteger("Debug", "Width", Width);
        RegFile->WriteInteger("Debug", "Level", DBLevel->ValueAsInt);
        RegFile->WriteInteger("Debug", "AutoScroll", AutoScroll->Checked);
        RegFile->WriteInteger("Debug", "TimeStamp", TimeStampGroup->ItemIndex);
        }
    __finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }
// save Debug Messages window always as rich text file
if ((MainForm->GlobalDBLevel >= 1) && Debug_Messages->Lines->Count > 0) {
	Debug_Messages->PlainText = false;
    const String FileName = String(MainForm->cStartDir) + "\\" + "Debug_Messages.rtf";
    Debug_Messages->Lines->SaveToFile(FileName);
    }

// save CRC Buffer window as either rich text or plain text file
if ((MainForm->GlobalDBLevel >= 1) && MainForm->CRCBuffer->Lines->Count > 0) {
	MainForm->CRCBuffer->PlainText = MainForm->PlainTextFile->Checked;
	if (MainForm->CRCBuffer->PlainText) {
    	const String FileName = String(MainForm->cStartDir) + "\\" + "CRC_Messages.txt";
		MainForm->CRCBuffer->Lines->SaveToFile(FileName);
		} else {
    	const String FileName = String(MainForm->cStartDir) + "\\" + "CRC_Messages.rtf";
		MainForm->CRCBuffer->Lines->SaveToFile(FileName);
		}
    
    }
// save TIU Buffer window as either rich text or plain text file
if ((MainForm->GlobalDBLevel >= 1) && MainForm->TIUBuffer->Lines->Count > 0) {
	MainForm->TIUBuffer->PlainText = MainForm->PlainTextFile->Checked;
	if (MainForm->TIUBuffer->PlainText) {
    	const String FileName = String(MainForm->cStartDir) + "\\" + "TIU_Messages.txt";
		MainForm->TIUBuffer->Lines->SaveToFile(FileName);
		} else {
    	const String FileName = String(MainForm->cStartDir) + "\\" + "TIU_Messages.rtf";
		MainForm->TIUBuffer->Lines->SaveToFile(FileName);
		}
    
    }
// save RFID Buffer window as either rich text or plain text file
if ((MainForm->GlobalDBLevel >= 1) && MainForm->RFIDBuffer->Lines->Count > 0) {
	MainForm->RFIDBuffer->PlainText = MainForm->PlainTextFile->Checked;
	if (MainForm->RFIDBuffer->PlainText) {
    	const String FileName = String(MainForm->cStartDir) + "\\" + "RFID_Messages.txt";
		MainForm->RFIDBuffer->Lines->SaveToFile(FileName);
		} else {
    	const String FileName = String(MainForm->cStartDir) + "\\" + "RFID_Messages.rtf";
		MainForm->RFIDBuffer->Lines->SaveToFile(FileName);
		}
    
    }

MainForm->bDebug = false;	// clear flag on exit
//
// Debug window is always opaque
//
}

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

void __fastcall TDB::ShowIt(void)
{
DebugTopPanel->Caption = "Debug Messages";
Caption = DebugTopPanel->Caption;
MainForm->bDebug = true;	// turn on debugging

//Setup->SuperTIUMode->Visible = true;
Setup->AllowZero->Visible = true;
Setup->CRC_Trace->Visible = true; 	// not saved in Registry
Setup->EncodedRecordFormat->Visible = true;

#ifdef DEBUGPANELWANTED
// turn on debug check box panel
Setup->SerialDebugPanel->Visible = true;

Setup->BaudRateSel->Enabled = true;
Setup->ParitySel->Enabled = true;
Setup->BitsSel->Enabled = true;
Setup->StopSel->Enabled = true;

// turn on debug version of port control
Setup->TIUPortListGroup->Visible = false;
Setup->BaudRateSel->Visible = true;
Setup->ParitySel->Visible = true;
Setup->StopSel->Visible = true;
Setup->ParitySel->Visible = true;
Setup->BitsSel->Visible = true;
#endif

SaveMainHint = MainForm->Hint;  // save main window hint for restore on exit
MainForm->Hint = "RTC Debug Mode - Use at your own risk";

// turn on labels on MainForm counters
//MainForm->ActiveLabel->Visible = true;
//MainForm->InActiveLabel->Visible = true;
//MainForm->RetriesLabel->Visible = true;
//MainForm->StartedLabel->Visible = true;
//if (Setup->WTIU_WiFi_Mode->Checked) {
//    MainForm->WTIULabel->Visible = true;
//    }

// both scroll bar only on EngineData StringGrid
MainForm->EngineData->ScrollBars = ssBoth;

// minumum width
Constraints->MinWidth = Width;

Show();  	      // Same as setting Visible to true

// Note: I want to have the window come up at the postion given by
// the saved coordinates. If I put the Show() call after this registry
// code, the Show() puts its own coordinates in.
// With the Show() before the registry code, the window flashes in
// the windows default position and then, at least, moves to its
// new position. I would like to get rid of the flashing but I don't know
// how
// Required for multiple monitors and save/restore position/size
Position=poDesigned;
DefaultMonitor=dmDesktop;
//
// read preferences from the Registry
RegFileRootKey = MainForm->RegFileRootKey;
if (MainForm->bSaveRegistryInfo) {
    // read Preferences from registry if they exist
    TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
    assert(RegFile != NULL);
    __try {
        Top = RegFile->ReadInteger("Debug", "Top", Top);
        Left = RegFile->ReadInteger("Debug", "Left", Left);
        // if using multiple monitors and one was removed, pull the window
        // back onto the remaining monitor
        int MC = Screen->MonitorCount;
        if (MC == 1) {
            int Wth = Screen->Monitors[0]->Width;
            if (Left > Wth) Left -= Wth;
            int Hth = Screen->Monitors[0]->Height;
            if (Top > Hth) Top -= Hth;
            }
        TopLeftPos = Point(Left, Top); // position of Find dialog
        Height = RegFile->ReadInteger("Debug", "Height", Height);
        Width = RegFile->ReadInteger("Debug", "Width", Width);
        DBLevel->ValueAsInt = RegFile->ReadInteger("Debug", "Level", DBLevel->ValueAsInt);
        DBLevel->Options = DBLevel->Options << eoLimitMin;	// necessary to repair some subtle bug in the AbNumSpin
        AutoScroll->Checked = RegFile->ReadInteger("Debug", "AutoScroll", AutoScroll->Checked);
        TimeStampGroup->ItemIndex = RegFile->ReadInteger("Debug", "TimeStamp", TimeStampGroup->ItemIndex);
        TimeStampMode = TimeStampGroup->ItemIndex;  // for global thread access
        }
    __finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }
//
TimeStampButtonClick(NULL);	// place a time stamp at the beginning of the debug messages window
Debug_Print("Version : " + MainForm->Caption);
Debug_Print("Date-Time : " + MainForm->Date_Time->Caption);   // display the program compile date/time/compiler in the debug messages window

/*
// Display the class name of the TRichEdit component
// https://stackoverflow.com/questions/32397734/get-version-of-rich-edit-library
// Seems useless, it displays "TRichEdit"
TCHAR className[MAX_PATH];
GetClassName(Debug_Messages->Handle, className, sizeof className);
Debug_Print("Class : " + String(className));
// https://devblogs.microsoft.com/oldnewthing/20101231-00/?p=11863
RealGetWindowClass(Debug_Messages->Handle, className, sizeof className);
Debug_Print("Class : " + String(className));
*/
MainForm->GlobalDBLevel = DBLevel->ValueAsInt;	// use this instead of the DBLevel->Value VCL property in threads
}
//---------------------------------------------------------------------------

void __fastcall TDB::HideButtonClick(TObject *Sender)
{
FindDialog1->CloseDialog();
// Hiding the form does not turn off debugging
//MainForm->bDebug = false;	// turn off debugging
//MainForm->GlobalDBLevel = 0;
//
Hide();
// close up mainform
MainForm->Width = MainForm->iSavedWidth;
MainForm->Height = MainForm->iSavedHeight;
// turn off horizontal scroll bar on EngineData StringGrid
MainForm->EngineData->ScrollBars = ssVertical;

Setup->AllowZero->Visible = false;
Setup->CRC_Trace->Visible = false; 	// not saved in Registry
Setup->EncodedRecordFormat->Visible = false;

// turn off debug check box panel
Setup->SerialDebugPanel->Visible = false;

Setup->BaudRateSel->Enabled = false;
Setup->ParitySel->Enabled = false;
Setup->BitsSel->Enabled = false;
Setup->StopSel->Enabled = false;

//Setup->COMSelGroup->Top = 18;	// Serial port box position
//Setup->SerialPortListGroup->Top = 18;

// turn off debug version of port control
Setup->TIUPortListGroup->Visible = true;
Setup->BaudRateSel->Visible = false;
Setup->ParitySel->Visible = false;
Setup->StopSel->Visible = false;
Setup->ParitySel->Visible = false;
Setup->BitsSel->Visible = false;

// turn off labels on MainForm counters
//MainForm->ActiveLabel->Visible = false;
//MainForm->InActiveLabel->Visible = false;
//MainForm->RetriesLabel->Visible = false;
//MainForm->StartedLabel->Visible = false;
//MainForm->WTIULabel->Visible = false;

MainForm->Hint = SaveMainHint;

// remove horizontal scroll bar on EngineData StringGrid
MainForm->EngineData->ScrollBars = ssVertical;
}
//---------------------------------------------------------------------------

bool __fastcall TDB::Debug_Print(String Msg, TColor Color)
{
if (!MainForm->bDebug && !Debug_Messages) return false;		// just in case the TRichEdit was destroyed
//
UI_Struct * Message1;
// type 1 message
Message1 = new UI_Struct;
Message1->PrintColor = Color;
Message1->Line = Msg;
//-----------------------------------
if (TimeStampMode == 0 || (TimeStampMode == 1 && PC_Tenths == 0)) {
    // https://www.drdobbs.com/windows/win32-performance-measurement-options/184416651
    // https://stackoverflow.com/questions/1695288/getting-the-current-time-in-milliseconds-from-the-system-clock-in-windows
    SYSTEMTIME st;
    GetLocalTime(&st);
    char currentTime[24];
    snprintf(currentTime, sizeof currentTime, "%02d:%02d:%02d.%03d", st.wHour, st.wMinute, st.wSecond , st.wMilliseconds);
    Message1->Line = String(currentTime) + " " + Message1->Line;
    } else {
    if (TimeStampMode == 1) {
        Message1->Line = Format(String("%9.1f"), ARRAYOFCONST((PC_Tenths/10.0))) + " " + Message1->Line;
        } else {
        if (TimeStampMode == 2) {
            float GTC = GetTickCount();
            Message1->Line = Format(String("%9.3f"), ARRAYOFCONST((GTC/1000.0))) + " " + Message1->Line;
            } else {
            Message1->Line = Format(String("%7d"), ARRAYOFCONST((MainForm->MsgNum))) + " " + Message1->Line;
            MainForm->MsgNum++;
            }
        }
    }
//-----------------------------------
// Post CM_UI message to MainForm for direct print to Debug_Messages RichEdit Control
(void) PostMessage(MainForm->Handle, CM_UI, M_PrintItem, (LPARAM) Message1);
// Message1 deleted in Train_Control.cpp
return true;
}
//---------------------------------------------------------------------------

void __fastcall TDB::CutDebugClick(TObject *Sender)
{
// Debug Buffer popup menu item
Debug_Messages->CutToClipboard();
}
//---------------------------------------------------------------------------

void __fastcall TDB::CopyDebugClick(TObject *Sender)
{
// Debug Buffer popup menu item
Debug_Messages->CopyToClipboard();
}
//---------------------------------------------------------------------------

void __fastcall TDB::PasteDebugClick(TObject *Sender)
{
// Debug Buffer popup menu item
Debug_Messages->PasteFromClipboard();
}
//---------------------------------------------------------------------------

void __fastcall TDB::SelectAllDebugClick(TObject *Sender)
{
// Debug Buffer popup menu item
Debug_Messages->SelectAll();
}
//---------------------------------------------------------------------------

void __fastcall TDB::SaveDebugClick(TObject *Sender)
{
if( Debug_Messages->Lines->Count == 0) return;	// buffer is empty

SaveDialog1->InitialDir = MainForm->WorkingDir;

String NameOfFile;
if (MainForm->PlainTextFile->Checked) {  // Plain text - make .txt file
	SaveDialog1->DefaultExt = String("txt");
	SaveDialog1->Filter = "Text files (*.txt)|*.txt";
    SaveDialog1->FileName = "Debug Messages.txt";
	} else {					// Rich text - make .rtf file
	SaveDialog1->DefaultExt = String("rtf");
	SaveDialog1->Filter = "Rich text files (*.rtf)|*.rtf";
	SaveDialog1->FileName = "Debug Messages.rtf";
	}
SaveDialog1->Title = "Save File";
SaveDialog1->Options = SaveDialog1->Options >> ofOverwritePrompt << ofEnableSizing;	// RTC will handle this case

if(SaveDialog1->Execute()) {
	NameOfFile = SaveDialog1->FileName;
    // Change working directory to whatever was selected
    MainForm->WorkingDir = ExtractFilePath(NameOfFile);
    // if file exists, confirm first
    if (FileExists(NameOfFile)) {
        // ask for confirmation
        int result = Application->MessageBox(TEXT("File Exists - Do you want to overwrite?"), Caption.c_str(), MB_YESNO | MB_ICONQUESTION);
    	// if No, exit
    	if (result  == IDNO) {
            return;
        	}
    	}
    // At this point, it's either a new file or the user has indicated overwrite
    // text only file if PlainText is checked
	bool savPlainText = Debug_Messages->PlainText;
	Debug_Messages->PlainText = MainForm->PlainTextFile->Checked;   // if Checked set PlainText to true
	Debug_Messages->Lines->SaveToFile(NameOfFile);
	if (DEBUGGINGX(1)) DB->Debug_Print("WritetoDisk: " + NameOfFile + " - " + String(Debug_Messages->Lines->Count) + " Lines");
	Debug_Messages->PlainText = savPlainText;
	return;	// done
	} else {
    return;	// user canceled
    }
}
//---------------------------------------------------------------------------

void __fastcall TDB::ClearDebugButtonClick(TObject *Sender)
{
// clear Debug Messages and line count
Debug_Messages->Clear();
}
//---------------------------------------------------------------------------

void __fastcall TDB::CopyButtonClick(TObject *Sender)
{
Debug_Messages->SelectAll();
Debug_Messages->CopyToClipboard();
Debug_Messages->SelLength = 0;	// clear selection highlight
}
//---------------------------------------------------------------------------

void __fastcall TDB::Font1Click(TObject *Sender)
{
FontDialog1->Options << fdApplyButton;
FontDialog1->Execute();
}
//---------------------------------------------------------------------------

void __fastcall TDB::FontDialog1Apply(TObject *Sender, HWND Wnd)
{
if (ActiveControl->ClassNameIs("TEdit"))
   ((TEdit *)ActiveControl)->Font->Assign(FontDialog1->Font);
  else if (ActiveControl->ClassNameIs("TRichEdit"))
    ((TRichEdit *)ActiveControl)->SelAttributes->Assign(FontDialog1->Font);
    else
    MessageBeep(0);
}
//---------------------------------------------------------------------------

void __fastcall TDB::FormKeyDown(TObject *Sender, WORD &Key,
      TShiftState Shift)
{
// Make sure KeyPreview is true for this form
assert(KeyPreview);
// Pass function keys to the MainForm, handle rest of the keys here.
switch (Key) {
    case VK_F1:
    case VK_F2:
    case VK_F3:
    case VK_F4:
    case VK_F5:
    case VK_F6:
    case VK_F7:
    case VK_F8:
    case VK_F9:
    case VK_F10:
    case VK_F11:
    case VK_F12:
		MainForm->FormKeyDown(Sender, Key, Shift);
        Key = 0;	// handled
		break;
    case 'F':
        if (Shift.Contains(ssCtrl)) {
            FindButtonClick(Sender);
            }
        break;
    case 'S':
        if (Shift.Contains(ssCtrl)) {
            SaveDebugClick(Sender);
            }
        break;
	}
}
//---------------------------------------------------------------------------

void __fastcall TDB::FormKeyPress(TObject *Sender, char &Key)
{
// CTRL-F
switch(Key) {
    case(0x06):
        FindButtonClick(Sender);
        break;
    default:
        break;
    }
}
//---------------------------------------------------------------------------

void __fastcall TDB::CopyrightClick(TObject *Sender)
{
// Copy the Form to the clipboard.
// In BCB4.0, there is no SetSize method associated with TBitmap
// so we have to use Width and Height separately.
//
// see:
// https://stackoverflow.com/questions/3190777/how-do-i-copy-a-form-as-an-image-to-the-clipboard
//
Graphics::TBitmap * bm;
bm = new Graphics::TBitmap;
try
	{
    #if __BORLANDC__ >= 0x720
	// Defined for CB10.1 C++ programs that use the VCL.
	bm->SetSize(ClientWidth, ClientHeight);
    #else
    bm->Width = ClientWidth;
    bm->Height = ClientHeight;
    #endif
	BitBlt(bm->Canvas->Handle, 0, 0, ClientWidth, ClientHeight, Canvas->Handle, 0, 0, SRCCOPY);
    Clipboard()->Assign(bm);
    }
__finally
	{
	delete bm;
    }
// close up mainform
//MainForm->Width = MainForm->iSavedWidth;
//MainForm->Height = MainForm->iSavedHeight;
}
//---------------------------------------------------------------------------

void __fastcall TDB::TimeStampButtonClick(TObject *Sender)
{
// place a time stamp in the Debug Buffer in the form "Thu Jan 20 15:19:21 2022"
  time_t rawtime;
  struct tm * timeinfo;

  time ( &rawtime );
  timeinfo = localtime ( &rawtime );
  Debug_Print("Timestamp : " + String(asctime (timeinfo)));
}
//---------------------------------------------------------------------------

void __fastcall TDB::DBLevelValueChanged(TObject *Sender)
{
Debug_Print("Debug Level changed to " + DBLevel->Text);
MainForm->GlobalDBLevel = DBLevel->ValueAsInt;	// keep global value in sync with VCL property
}
//---------------------------------------------------------------------------

void __fastcall TDB::FormCloseQuery(TObject *Sender, bool &CanClose)
{
// ask user to use the Hide button instead of the X button
MainForm->DebugMessagePopup("Warning", "Use the [Hide] button to close the Debug window", "");
CanClose = false;	// disallow close
}
//---------------------------------------------------------------------------

void __fastcall TDB::ToggleWordWrapClick(TObject *Sender)
{
Debug_Messages->WordWrap = !Debug_Messages->WordWrap;
ToggleWordWrap->Checked = Debug_Messages->WordWrap;
}
//---------------------------------------------------------------------------

void __fastcall TDB::WMEnable(TWMEnable &Message)
// https://community.idera.com/developer-tools/b/blog/posts/keep-a-non-modal-window-active-when-a-modal-window-is-being-shown
{
// Handle Message : WM_ENABLE
if (!Message.Enabled) {
	PostMessage(Handle, CM_REENABLE, 1, 0);
    }
//
}
//---------------------------------------------------------------------------

void __fastcall TDB::CMReenable(TMessage &Message)
{
// Handle Message : Reenable window CM_REENABLE
//
EnableWindow(Handle, true);
//
}
//---------------------------------------------------------------------------

void __fastcall TDB::AutoScrollClick(TObject *Sender)
{
//
// remain at end of buffer
//
if (AutoScroll->Checked) {
    ScrollEnd(Debug_Messages);
    }
}
//---------------------------------------------------------------------------
// https://learn.microsoft.com/en-us/windows/win32/controls/em-findtext
/*
Microsoft Rich Edit 2.0 and later: If set, the search is from the end
of the current selection to the end of the document. If not set, the
search is from the end of the current selection to the beginning of the document.
Microsoft Rich Edit 1.0: The FR_DOWN flag is ignored. The search is
always from the end of the current selection to the end of the document.
*/

void __fastcall TDB::FindDialog1Find(TObject *Sender)
{
#ifdef FIND_UP_DOES_NOT_WORK_WITH_THIS_CODE
  int FoundAt, StartPos, ToEnd;
  TSearchTypes mySearchTypes = TSearchTypes();
  // Begin the search after the current selection,
  // if there is one.
  // Otherwise, begin at the start of the text.
  if (FindDialog1->Options.Contains(frMatchCase))
	mySearchTypes << stMatchCase;
  if (FindDialog1->Options.Contains(frWholeWord))
	mySearchTypes << stWholeWord;
  if (Debug_Messages->SelLength)
	StartPos = Debug_Messages->SelStart + Debug_Messages->SelLength;
  else
	StartPos = 0;
  // ToEnd is the length from StartPos
  // to the end of the text in the rich edit control.
  ToEnd = Debug_Messages->Text.Length() - StartPos;
  FoundAt = Debug_Messages->FindText(FindDialog1->FindText, StartPos, ToEnd, mySearchTypes);
  if (FoundAt != -1)
  {
	Debug_Messages->SetFocus();
	Debug_Messages->SelStart = FoundAt;
	Debug_Messages->SelLength = FindDialog1->FindText.Length();
  }
  else Beep();
#else
long FoundAt;
long StartPos, FindLength;

TSearchTypes TheSearchTypes = TSearchTypes();

if (FindDialog1->Options.Contains(frDown)) {
    if (Debug_Messages->SelLength)
	    StartPos = Debug_Messages->SelStart + Debug_Messages->SelLength;
    else
	    StartPos = 0;
    FindLength = Debug_Messages->Text.Length() - StartPos;
    } else {
    StartPos = Debug_Messages->SelStart;
    FindLength = -Debug_Messages->SelStart;
    }

if (FindDialog1->Options.Contains(frMatchCase)) {
    TheSearchTypes << stMatchCase;
    }

if (FindDialog1->Options.Contains(frWholeWord)) {
    TheSearchTypes << stWholeWord;
    }

FoundAt = MyFindText(FindDialog1->FindText, StartPos, FindLength, TheSearchTypes,
    FindDialog1->Options.Contains(frDown));

if (FoundAt != -1) {
	Debug_Messages->SetFocus();
	Debug_Messages->SelStart = FoundAt;
	Debug_Messages->SelLength = FindDialog1->FindText.Length();
  }
  else Beep();
#endif
}
//---------------------------------------------------------------------------
//
// Replacement for Debug->Messages->FindText
// https://www.tek-tips.com/threads/tfinddialog-and-finding-text-in-an-upward-direction.397710/
//
int __fastcall TDB::MyFindText(String SearchStr, long StartPos, long FindLength, TSearchTypes Options, bool SearchDown)
{
TFindText Find;
Word Flags;
int Result = -1;
/*
The cpMin member of FINDTEXT.chrg always specifies the starting-point of the search,
and cpMax specifies the end point. When searching backward, cpMin must be equal to
or greater than cpMax. When searching forward, a value of -1 in cpMax extends the
search range to the end of the text.
*/
Find.chrg.cpMin = StartPos;
Find.chrg.cpMax = StartPos + FindLength;
Find.lpstrText = SearchStr.c_str();

Flags = 0;

if (Options.Contains(stWholeWord)) {
    Flags = Flags | FT_WHOLEWORD;
    } else {
    Flags = Flags & ~FT_WHOLEWORD;
    }
if (Options.Contains(stMatchCase)) {
    Flags = Flags | FT_MATCHCASE;
    } else {
    Flags = Flags & ~FT_MATCHCASE;
    }

if (SearchDown) {
    Flags = Flags | FR_DOWN;
    } else {
    Flags = Flags & ~FR_DOWN;     // Search Up, just does not work !
    //Flags = Flags | FR_DOWN;
    }

Result = SendMessage(Debug_Messages->Handle, EM_FINDTEXT, Flags, (long) &Find);

return Result;
}
//---------------------------------------------------------------------------

void __fastcall TDB::FindButtonClick(TObject *Sender)
{
FindDialog1->Position = TopLeftPos;    // position of Find dialog
// for BCB 4.0, remove the Up and Down Buttons from the dialog
#if __BORLANDC__ < 0x560
FindDialog1->Options << frHideUpDown;
#endif
FindDialog1->Execute();
}
//---------------------------------------------------------------------------

void __fastcall TDB::TimeStampGroupClick(TObject *Sender)
{
// Make this value accessible to non VCL threads
TimeStampMode = TimeStampGroup->ItemIndex;
}
//---------------------------------------------------------------------------

void __fastcall TDB::FindDialog1Close(TObject *Sender)
{
TopLeftPos = FindDialog1->Position;        // new position of Find Dialog
}
//---------------------------------------------------------------------------

