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

© Copyright 2015 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/>.

    --------------------------------------------------------------

    12th York DCS User Group Meeting - Summary
    https://ogrforum.ogaugerr.com/topic/12th-york-dcs-user-group-meeting---summary

    Q – What does the signal strength on the remote measure?

    A – The numerical output on the remote’s screen equates the amount of data
    being transmitted and received between the remote and the engine into a
    number. It isn’t a percentage but just posts a number based on the
    transmissions. For example, if 100 data packets are sent out and 96-100
    are received back the remote will post a 10. If 100 are sent out and 89-95
    are received back a 9 will be displayed. This will change with the amounts.

    --------------------------------------------------------------

    The following is from The DCS O Gauge Companion 2nd Edition, page 55:

    DCS Signal Strength

    DCS signal strength is a number, from 1-10, that's calculated based on the
    number of data packets that the TIU sends and receives back from a PS2 engine
    that's performing a DCS signal strength test.

    During the test, the TIU sends a continuous series of data packets to the PS2
    engine. The PS2 engine is expected to reply to every packet that it receives,
    however, some packets are either not received by the engine or the response is
    not received by the TIU. Regardless, the TIU calculates DCS signal strength by
    counting how many response packets the TIU receives from the PS2 engine out of
    each hundred data packets the TIU sent, and then looks at a sliding scale to
    determine the signal strength that should be reported. The scale is not linear,
    rather, it is such that 87-100 packets equates to a DCS signal strength of “10”,
    80-86 equates to a “9”, and so on.

    --------------------------------------------------------------

*/
// Home Page : http://www.silogic.com/trains/RTC_Running.html
//---------------------------------------------------------------------------
#include <vcl.h>
#include <tchar.h>
#pragma hdrstop

#include "Engine_Commands.h"
#include "Train_Control.h"
#include "AIU_Control.h"

#include "Op_Control.h"

#include "Track_Signal_Thread.h"
#include "Track_Signal.h"
#include "Volume_Control.h"
#include "Tserial_event.h"
#include "Utility.h"
#include "Setup_Control.h"
#include "Comm_Thread.h"
#include "Debug_Controls.h"
#include "Clipbrd.hpp"

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

extern bool SuperTIUMode, TIUSuperMode[TIUMAX];         // Flags to indicate if Super TIU Mode is active
extern TCommThread * ptrTIUCommThread;				// pointer to TIU communications thread

// NOTE: this is public static variable, you can never use
// this when you create multiple instances of a Form
//TTrackSignal *TrackSignal;

//---------------------------------------------------------------------------
__fastcall TTrackSignal::TTrackSignal(TComponent* Owner)
	: TForm(Owner)
{
// never called since I used the overloaded constructor below
assert(false);	// never called since I used the overloaded constructor below
}
//---------------------------------------------------------------------------
__fastcall TTrackSignal::TTrackSignal(TComponent* Owner, HWND Parent, int Remote, int Engine, int TIU, String EngName)
    : TForm(Owner)
{
// Calling Operations Screen
//CallingOpScreen = (TOp *) Owner;
CallingOpScreen = dynamic_cast<TOp *>(Owner);
if (DEBUGGING) SoundLED->Visible = true;
if (CallingOpScreen) SoundLED->Checked = CallingOpScreen->SoundsSwitch->Checked;
// grab the Remote, Engine, TIU numbers and Engine Name
RemoteNo = Remote;
assert(RemoteNo >= 0 && RemoteNo <= REMOTEMAX);
EngineNo =  Engine;
assert(EngineNo >= (Setup->AllowZero->Checked? 0 : 1) && EngineNo <= ENGINEMAX);	// 0 factory reset, 1-99
TIUNo = TIU;
assert(TIUNo >= 1 && TIUNo <= TIUMAX);
EngineName = EngName;
ParentHandle = Parent;

LocalTransparencyOverride = false;
ptrTrackSignalThread = NULL;

//
if (Setup->DisableEmergencyStopButton->Checked) {
	ESTOP->Visible = false;
    ESTOP->Enabled = false;
    ESTOP->Hint = "Disabled in Setup";
    ESTOP->Cursor = crNo;
    }
Track_Signal_Label->Caption = "Engine " + String(EngineNo) + " - " +
		EngineName + (SuperTIUMode && TIUSuperMode[TIUNo-1] ? String(" SuperTIUMode") : " - TIU " + String(TIUNo));
//

#if __BORLANDC__ >= 0x720		// CB10.1 Only
FTS->Alignment = taCenter;
FTSAverage->Alignment = taCenter;
#endif
// Read preferences from the Registry
RegFileRootKey = MainForm->RegFileRootKey;
// Read Preferences from registry if they exist
TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
assert(RegFile != NULL);
__try {
	UpdateRateSpin->ValueAsInt = RegFile->ReadInteger("TrackSignal", "TrackSignalCount" + String(TIUNo) + String(EngineNo), UpdateRateSpin->ValueAsInt);
    }
__finally {
	// Deallocate RegFile
	delete RegFile;
    }
StartButton->Enabled = true;	// enable the start button
StopButton->Enabled = false;
}
//---------------------------------------------------------------------------

__fastcall TTrackSignal::~TTrackSignal(void)
{
// Stop the Track Signal Thread
if (ptrTrackSignalThread) {
	ptrTrackSignalThread->Terminate();	// thread will not free on terminate
    ptrTrackSignalThread->WaitFor(); 	// using WaifFor() with FreeOnTerminate causes "the handle is invalid (6)" error
    delete ptrTrackSignalThread;		// Free thread memory
    ptrTrackSignalThread = NULL;
    }
if (MainForm->bSaveRegistryInfo) {
    // write preferences to the Registry
    TRegIniFile * const RegFile = new TRegIniFile(RegFileRootKey);
    assert(RegFile != NULL);
    __try {
        RegFile->WriteInteger("TrackSignal", "TrackSignalCount" + String(TIUNo) + String(EngineNo), UpdateRateSpin->ValueAsInt);
        }
    __finally {
        // Deallocate RegFile
        delete RegFile;
        }
    }
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::StartButtonClick(TObject *Sender)
{
//
if (ptrTrackSignalThread) return;	// thread already running
// Start Thread
ptrTrackSignalThread = new TTrackSignalThread(true, this, Handle, CallingOpScreen, EngineNo, TIUNo, RemoteNo, UpdateRateSpin->ValueAsInt * 1000L, true);
// FreeOnTerminate is not set in constructor so we must delete the thread
assert(ptrTrackSignalThread != NULL);				// make sure that construction worked
StartButton->Enabled = false;
StopButton->Enabled = true;
HideButton->Enabled = false;
FeatureResetButton->Enabled = false;
LED->Checked = true;
LED->LED->ColorOn = clLime;
//
#if __BORLANDC__ >= 0x720
ptrTrackSignalThread->Start();			// start the thread for CB10.1
#else
ptrTrackSignalThread->Resume();			// start the thread for BCB4.0 - use deprecated in CB10.1, use Start() instead
#endif
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::StopButtonClick(TObject *Sender)
{
// Stop Thread
if (ptrTrackSignalThread) {
	ptrTrackSignalThread->Terminate();	// thread will not free on terminate
    ptrTrackSignalThread->WaitFor(); 	// using WaifFor() with FreeOnTerminate causes "the handle is invalid (6)" error
    delete ptrTrackSignalThread;		// Free thread memory
    ptrTrackSignalThread = NULL;
    }
StartButton->Enabled = true;
StopButton->Enabled = false;
HideButton->Enabled = true;
FeatureResetButton->Enabled = true;
LED->Checked = false;
// insert divider
Signals->DataPoint((TColor)0x000000, 100);
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::HideButtonClick(TObject *Sender)
{
Hide();	      // Same as setting Visible to false
StopButtonClick(Sender);	// stop updates and terminate thread
}
//---------------------------------------------------------------------------
// Workaround for SetLayeredWindowAttributes() to make transparent windows
extern lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::FormActivate(TObject *Sender)
{
#if __BORLANDC__ >= 0x720      // CB10.1 only
AlphaBlend = false; // Remove Transparency
#else
// Remove WS_EX_LAYERED from this window styles
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// Ask the window and its children to repaint
//Repaint();
#endif
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::FormDeactivate(TObject *Sender)
{
if (Setup->TransparentSpin->Value == Setup->TransparentSpin->MaxValue) return;	// Off
if (LocalTransparencyOverride) return;
#if __BORLANDC__ >= 0x720      // CB10.1 only
AlphaBlendValue = (255 * Setup->TransparentSpin->Value) / 100;
AlphaBlend = true;  // Add Transparency
#else
// Set WS_EX_LAYERED on this window
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) | WS_EX_LAYERED);
//SetLayeredWindowAttributes
m_pSetLayeredWindowAttributes(Handle, NULL, (255 * Setup->TransparentSpin->Value) / 100, LWA_ALPHA);
#endif
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::Track_Signal_LabelClick(TObject *Sender)
{
LocalTransparencyOverride = !LocalTransparencyOverride;	// Toggle transparency locally
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::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;
    }
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::TSMaximumClick(TObject *Sender)
{
Signals->Kind = (Signals->Kind == pgBar ? pgLine : pgBar); 
}
//---------------------------------------------------------------------------


void __fastcall TTrackSignal::ResetButtonClick(TObject *Sender)
{
// clear the packet counters
if (ptrTrackSignalThread) {
    ptrTrackSignalThread->ClearCounts();
    }
// insert divider
Signals->DataPoint((TColor)0x000000, 100);
}
//---------------------------------------------------------------------------

void __fastcall TTrackSignal::FeatureResetButtonClick(TObject *Sender)
{
if (EngineNo <= 0 || EngineNo >= ALLENGINE) return;	// Engines only

if (!MainForm->IsTIUConnected()) {
	MainForm->DebugMessagePopup("Warning", MainForm->ConnectMsg, "");
	return;
    }

if (EngineNo >= ALLENGINE) return;	// can't reset All Engine
// Engine Feature Reset
String FeatureResetMsg = "Are you sure you want to Feature Reset engine " + String(EngineNo) + " - " +
	MainForm->EngineData->Cells[ENG_NAME][EngineNo] + "?";
int retv = Application->MessageBox(FeatureResetMsg.c_str(),	TEXT("Warning"), MB_YESNO | MB_ICONQUESTION);
if (retv != IDYES) {
    return;
    }
(void)ptrTIUCommThread->Send_String_FIFO(EngineNo, "F0", TIUNo, RemoteNo, 11, VERY_LONG_WAIT, RECORD_OK);
// Feature reset turns Smoke on so you must turn it off if you want it off.
}
//---------------------------------------------------------------------------

