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

#include "DetectedTags.h"
#include "RTC_NFC.h"
#include "Train_Control.h"
#include "Setup_Control.h"
#include "Debug_Controls.h"
#include "Clipbrd.hpp"

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

TDetectedRFIDTags *DetectedRFIDTags;

//---------------------------------------------------------------------------
// by saeed_falahat
// https://stackoverflow.com/questions/122616/how-do-i-trim-leading-trailing-whitespace-in-a-standard-way
void trim_str(char *s)
{
    const size_t s_len = strlen(s);
    size_t i;
    for (i = 0; i < s_len; i++) {
        if (!isspace( (unsigned char) s[i] )) break;
	    }

    if (i == s_len) {
        // s is an empty string or contains only space characters
        s[0] = '\0';
	    } else {
    	    // s contains non-space characters
	        const char *non_space_beginning = s + i;
    	    char *non_space_ending = s + s_len - 1;
	        while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;
    	    size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;
        	if (s != non_space_beginning) {
	            // Non-space characters exist in the beginning of s
    	        memmove(s, non_space_beginning, trimmed_s_len);
        		}
	        s[trimmed_s_len] = '\0';
    	}
}
//---------------------------------------------------------------------------
__fastcall TDetectedRFIDTags::TDetectedRFIDTags(TComponent* Owner)
	: TForm(Owner)
{
//https://stackoverflow.com/questions/6049053/increase-size-limit-of-trichedit
// expand the size of RichEdit control
TagsDetectedList->MaxLength = System::MaxInt-2;
UIDTagsDetectedList->MaxLength = System::MaxInt-2;

UIDPacketStringList = new TStringList;
assert(UIDPacketStringList != 0);	// make sure constructor worked
UIDPacketStringList->Clear();
UIDPacketStringList->Sorted = true;
UIDPacketStringList->Capacity = 100;
// Prevent duplicate strings, so no need for us to check for duplicates when Add()ing to TagPacketStringList
#if __BORLANDC__ >= 0x720
UIDPacketStringList->Duplicates = System::Types::dupIgnore;		// CB10.1
#else
UIDPacketStringList->Duplicates = dupIgnore;					// BCB4.0
#endif
// TagPacketStringList is used by every PC window

TagPacketStringList = new TStringList;
assert(TagPacketStringList != 0);	// make sure constructor worked
TagPacketStringList->Clear();
TagPacketStringList->Sorted = true;
TagPacketStringList->Capacity = 100;
// Prevent duplicate strings, so no need for us to check for duplicates when Add()ing to TagPacketStringList
#if __BORLANDC__ >= 0x720
TagPacketStringList->Duplicates = System::Types::dupIgnore;		// CB10.1
#else
TagPacketStringList->Duplicates = dupIgnore;					// BCB4.0
#endif
// TagPacketStringList is used by every PC window
// RFIDFIFO & Tags used only by Program Control window
if (FileExists(String(MainForm->cStartDir) + "\\" + "Tags.ini")) TagPacketStringList->LoadFromFile(String(MainForm->cStartDir) + "\\" + "Tags.ini");	// load previously found Tags
if (TagPacketStringList->Count > 0) {
    // display the saved tags in the UI
    for (int iDex = 0 ; iDex < TagPacketStringList->Count ; iDex++) {
        UniqueTagCount->Value++;	// Keep global count of unique tags found
        TagsDetectedList->Lines->Append(TagPacketStringList->Strings[iDex].SubString(1,8) + "  " +
        	TagPacketStringList->Strings[iDex].SubString(9,9+32));		// Display in Tag Detection List
        }
    }
TagPageControl->ActivePage = DataTabSheet;
LocalTransparencyOverride = false;
}
//---------------------------------------------------------------------------
__fastcall TDetectedRFIDTags::~TDetectedRFIDTags(void)
{
if (TagPacketStringList->Count > 0) {
	TagPacketStringList->SaveToFile(String(MainForm->cStartDir) + "\\" + "Tags.ini");	// Save all Tags found
	}
delete TagPacketStringList;
TagPacketStringList = NULL;
delete UIDPacketStringList;
UIDPacketStringList = NULL;
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::HideButtonClick(TObject *Sender)
{
Hide();	      // Same as setting Visible to false	
}
//---------------------------------------------------------------------------

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

void __fastcall TDetectedRFIDTags::FormShow(TObject *Sender)
{
if (DEBUGGING) {
	ClearButton->Visible = true;
	UIDClearButton->Visible = true;
    }
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::ClearButtonClick(TObject *Sender)
{
TagCount->Value = 0;
RespCount->Value = 0;
TagSubCount->Value = 0;
TagSubCount->ColorDefault = clInactiveBorder;
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::Add(String Tag)
{
int Index;
assert(Tag.Length() == 40);
// Add the Tag to the TagPacketStringList and to the TagsDetectedList as needed
if (TagPacketStringList->Count == 0) {
    TagPacketStringList->Add(Tag);		// just add it
    UniqueTagCount->Value = 1;
    TagsDetectedList->Lines->Append(Tag.SubString(1,8) + "  " + Tag.SubString(9,9+32));		// Display in Tag Detection List
    // successfully added to the TList
    } else {
	// check if the tag is already in the string list, then it is already in the TagsDetectedList
    if (!TagPacketStringList->Find(Tag, Index)) {
	    // if not in the TagPacketStringList, add it to the TagPacketStringList and to the TagsDetectedList
	    TagPacketStringList->Add(Tag);
        UniqueTagCount->Value++;	// Keep global count of unique tags found
        TagsDetectedList->Lines->Append(Tag.SubString(1,8) + "  " + Tag.SubString(9,9+32));	// Display in Tag Detection List
        // successfully added to the TList
    	}
    }
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::AddUID(String UID)
{
int Index;
assert(UID.Length() == 8);
// Add the UID to the UIDPacketStringList and to the UIDTagsDetectedList as needed
if (UIDPacketStringList->Count == 0) {
    UIDPacketStringList->Add(UID);		// just add it
    UIDUniqueTagCount->Value = 1;
    UIDTagsDetectedList->Lines->Append(UID);		// Display in UIDTagsDetectedList
    // successfully added to the TList
    } else {
	// check if the UID is already in the string list, then it is already in the UIDTagsDetectedList
    if (!UIDPacketStringList->Find(UID, Index)) {
	    // if not in the TagPacketStringList, add it to the UIDPacketStringList and to the UIDTagsDetectedList
		UIDUniqueTagCount->Value++;	// Keep global count of unique tags found
        UIDTagsDetectedList->Lines->Append(UID);		// Display in UID Tag Detection List
        // successfully added to the TList
    	}
    }
}
//---------------------------------------------------------------------------

String __fastcall TDetectedRFIDTags::LookUpTagPacket(String UID)
{
// Lookup the 8 byte UID in all of the tags in the TagPacketStringList and return
// The 40 byte UID+Packet if found, otherwise return "".
//
// Note : use .IsEmpty() to check for returned "" string
//
// This performs a function similar to the TStringList->Find() function except
// that it indexes on only the first 8 bytes of UID
//
if (DEBUGGINGX(3)) {
	// display the UID of the tag to be saved
    const String smsgBuf = Format((String)"Lookup Tag UID (%8.8s)", ARRAYOFCONST((UID.c_str())));
    DB->Debug_Print(smsgBuf, clGreen);	            // Message to Debug Messages
    }
int Count = TagPacketStringList->Count;
if (Count == 0) return "";	// not found

assert(UID.Length() == 8);	// UID must be exactly 8 characters

for (int i = 0 ; i < Count ; i++) {
    if (UID == TagPacketStringList->Strings[i].SubString(1, 8)) {
        return TagPacketStringList->Strings[i].SubString(1, 40);	// found
        }
	}
return "";	// not found
}
//---------------------------------------------------------------------------

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

//---------------------------------------------------------------------------
// Workaround for SetLayeredWindowAttributes() to make transparent windows
extern lpfnSetLayeredWindowAttributes m_pSetLayeredWindowAttributes;
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::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);
#endif
//
// visualize the response packet count
if (DEBUGGING) {
	RespLabel->Visible = true;
    RespCount->Visible = true;
    }
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::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 TDetectedRFIDTags::EngineData_LabelClick(TObject *Sender)
{
LocalTransparencyOverride = !LocalTransparencyOverride;	// Toggle transparency locally	
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::Tag_LabelClick(TObject *Sender)
{
LocalTransparencyOverride = !LocalTransparencyOverride;	// Toggle transparency locally
}
//---------------------------------------------------------------------------
/**
 * charToHex
 *
 * 'ch' Character to be converted to hexadecimal
 *
 * Returns:
 *  Hex value, 0x00 on bad hex character
 */
//---------------------------------------------------------------------------
inline byte charToHex(byte ch)
{
byte val;
if (ch >= 'A' && ch <= 'F')
    val = ch - 55;
    else if (ch >= 'a' && ch <= 'f')
      val = ch - 87;
      else if (ch >= '0' && ch <= '9')
         val = ch - 48;
         else
         val = 0x00;
return val;
}
//---------------------------------------------------------------------------
/* Hex2Digit_to_Decimal
 *
 * convert a two digit hex character string into an 8 bit unsigned decimal number
 */
//--------------------------------------------------------------------------
byte Hex2Digit_to_Decimal(unsigned char * str) {
// convert a 2 digit char string to decimal
byte val = (charToHex(*str) << 4) & 0xF0;
val |= charToHex(*(str + 1)) & 0x0F;
return val;
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::TagsDetectedListMouseDown(
      TObject *Sender, TMouseButton Button, TShiftState Shift, int X,
      int Y)
{
// change the Description to show some information about the tag

TPoint pt = TagsDetectedList->CaretPos;
int idx = pt.y;
if (idx >= TagsDetectedList->Lines->Count) {
	Description->Caption = "";
	return;	// check limit
    }
//
#ifdef COMPLICATEDWAY
    TRichEdit* RichEdit = dynamic_cast<TRichEdit*>(Sender);
    if (!RichEdit) return;

    POINT pt1;
    pt1.x = X;
    pt1.y = Y;
    // 1. Get the character index from the clicked coordinates
    long charIndex = SendMessage(RichEdit->Handle, EM_CHARFROMPOS, 0, (LPARAM)&pt1);
    // 2. Get the line index from the character index
    long lineIndex = SendMessage(RichEdit->Handle, EM_LINEFROMCHAR, charIndex, 0);
    // 3. Get the start and length of the line
    long lineStartCharIndex = SendMessage(RichEdit->Handle, EM_LINEINDEX, lineIndex, 0);
    long lineLength = SendMessage(RichEdit->Handle, EM_LINELENGTH, lineStartCharIndex, 0);
    // 4. Highlight the line by setting selection and attributes
    // Set the selection to the current line
    // Clear any previous selection/highlight (optional, depends on desired behavior)
    RichEdit->SelStart = 0;
    RichEdit->SelStart = 44 * pt.y; //lineStartCharIndex;
    RichEdit->SelLength = 44;//lineLength;
    // Set the highlight color (foreground text color)
    RichEdit->SelAttributes->Color = clBlue; // Or any TColor you prefer
# else
// Highlight the line by setting selection
// Set the selection to the current line
// This simple code works because each line of equal length
TagsDetectedList->SelStart = 44 * pt.y;     // Start character for highlighting
TagsDetectedList->SelLength = 44;           // Length of highlighting
#endif
//
UP TagData;
TCHAR msgBuf[128];
// convert 32 byte tag character data to 16 byte binary data
for (int iDex = 0 ; iDex < 16; iDex++) {
	byte char2string[3];
	// char2string will contain two characters which make up one byte of the tag data
	// onebyte must be AnsiString because the c_str() taken needs to be a char type.
	AnsiString onebyte = TagsDetectedList->Lines->Strings[idx].SubString(1 + (iDex * 2) + 10, 2);
    strncpy (char2string, onebyte.c_str() , 2);
    // Convert the 2 characters to binary
    TagData.Byte_Array[iDex + 10] = Hex2Digit_to_Decimal(char2string);
	}
// the following code is not very elegant but works	
char CarNumber[7];
memcpy(CarNumber, &TagData.Data_Packet.EngineData.n1, 6);
CarNumber[6] = '\0';	// NULL terminate
trim_str(CarNumber);
String sCarNumber = String(CarNumber);
if (TagData.Data_Packet.EngineData.EngineNo == NOT_AN_ENGINE) {
    // Not an Engine (a regular car)
	_sntprintf(msgBuf, sizeof msgBuf, TEXT("Car %s %s #%s"),
        MainForm->RailroadNameComboBox->Items->Strings[TagData.Data_Packet.EngineData.Railroad].c_str(),
        MainForm->CarModelComboBox->Items->Strings[TagData.Data_Packet.EngineData.CarModel].c_str(),
		sCarNumber.c_str());
    } else {
    // Is an engine (Incorrect code 80 in early tags)
    if (TagData.Data_Packet.EngineData.TagLocation == 80) TagData.Data_Packet.EngineData.TagLocation = TAG_UNKNOWN;
	_sntprintf(msgBuf, sizeof msgBuf, TEXT("Engine #%d %s %s #%s %s"),
        TagData.Data_Packet.EngineData.EngineNo,
        MainForm->RailroadNameComboBox->Items->Strings[TagData.Data_Packet.EngineData.Railroad].c_str(),
		MainForm->EngineModelComboBox->Items->Strings[TagData.Data_Packet.EngineData.CarModel].c_str(),
		sCarNumber.c_str(),
        MainForm->TagLocationComboBox->Items->Strings[TagData.Data_Packet.EngineData.TagLocation].c_str()
        );
    }
Description->Caption = msgBuf;
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::TagsDetectedListKeyDown(TObject *Sender,
      WORD &Key, TShiftState Shift)
{
Key = 0;	// Ignore key presses
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::TagsDetectedListKeyUp(TObject *Sender,
      WORD &Key, TShiftState Shift)
{
Key = 0;	// Ignore key presses
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::TagsDetectedListKeyPress(
      TObject *Sender, char &Key)
{
Key = 0;	// Ignore key presses
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::CutClick(TObject *Sender)
{
TagsDetectedList->CutToClipboard();
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::CopyClick(TObject *Sender)
{
TagsDetectedList->CopyToClipboard();	
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::PasteClick(TObject *Sender)
{
TagsDetectedList->PasteFromClipboard();	
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::UndoClick(TObject *Sender)
{
TagsDetectedList->Undo();
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::SelectAllClick(TObject *Sender)
{
TagsDetectedList->SelectAll();	
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::UIDClearButtonClick(TObject *Sender)
{
UIDTagCount->Value = 0;
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::UIDCopyButtonClick(TObject *Sender)
{
UIDTagsDetectedList->SelectAll();
UIDTagsDetectedList->CopyToClipboard();
}
//---------------------------------------------------------------------------

void __fastcall TDetectedRFIDTags::MoreInformation1Click(TObject *Sender)
{
HINSTANCE retv = ShellExecute(NULL, TEXT("open"), TEXT("http://www.silogic.com/trains/RFID.html"), NULL, NULL, SW_SHOWNORMAL);
if ((int)retv <= 32) {
	// error
    if (DEBUGGING) DB->Debug_Print("Shell Execute error - return value = " + String((int) retv));
    }
}
//---------------------------------------------------------------------------

