/*
  ICS HTTP Threaded Test - C++Builder translation of the Delphi demo OverbyteIcsHttpThr1.pas
  (demos-delphi-vcl), ICS V9.8.

  Starts up to six worker threads (see OverbyteIcsHttpThr2.cpp), each doing a synchronous HTTPS
  GET of the URL with its own TSslHttpRest, and shows the page text and thread states.

  How this C++ version differs from the Delphi original, apart from syntax:
  - The threads are freed in the form destructor. The Delphi OnDestroy handler is not bound in
    the .dfm: a C++ form runs OnDestroy from the VCL base destructor, after its own members are gone.
*/

#include <vcl.h>
#pragma hdrstop

#include "OverbyteIcsHttpThr1.h"

#include <memory>

#include "OverbyteIcsIniFiles.hpp"
#include "OverbyteIcsUtils.hpp"
#include "OverbyteIcsTypes.hpp"

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

THttpThreadForm *HttpThreadForm;

namespace {

const String SectionData = "Data";
const String KeyURL = "URL";
const String KeyProxy = "Proxy";
const String SectionWindow = "Window";
const String KeyTop = "Top";
const String KeyLeft = "Left";
const String KeyWidth = "Width";
const String KeyHeight = "Height";

// Holds the thread's log list locked for the length of a scope.
class TLogListLock
{
public:
    explicit TLogListLock(THTTPThread *AThread) : FThread(AThread), FList(AThread->LockLogList()) {}
    ~TLogListLock() { FThread->UnlockLogList(); }
    TStringList *List() const { return FList; }
private:
    THTTPThread *FThread;
    TStringList *FList;
};

}  // namespace

__fastcall THttpThreadForm::THttpThreadForm(TComponent *Owner)
    : TForm(Owner), FInitialized(false), FTerminating(false)
{
    std::fill(std::begin(FThreads), std::end(FThreads), nullptr);
}

// Terminates and frees every thread; this waits for each thread to finish its request.
__fastcall THttpThreadForm::~THttpThreadForm()
{
    FTerminating = true;
    for (THTTPThread *Thread : FThreads)
        delete Thread;
}

void __fastcall THttpThreadForm::FormCreate(TObject *Sender)
{
    Caption = Caption + IcsVerLitSample;   // common ICS sample caption: version and date
    FIniFileName = GetIcsIniFileName(true);
    for (int I = 0; I < MaxThreads; I++)
        DisplayThreadState(I, tsInvalid);   // none created yet
    ResultsMemo->Clear();
}

void __fastcall THttpThreadForm::FormShow(TObject *Sender)
{
    if (!FInitialized) {
        FInitialized = true;
        std::unique_ptr<TIcsIniFile> IniFile(new TIcsIniFile(FIniFileName, true));
        URLEdit->Text = IniFile->ReadString(SectionData, KeyURL,
                                            "https://wiki.overbyte.eu/wiki/index.php/FAQ_Using_TSslHttpRest");
        ProxyEdit->Text = IniFile->ReadString(SectionData, KeyProxy, "");
        Top = IniFile->ReadInteger(SectionWindow, KeyTop, Top);
        Left = IniFile->ReadInteger(SectionWindow, KeyLeft, Left);
        Width = IniFile->ReadInteger(SectionWindow, KeyWidth, Width);
        Height = IniFile->ReadInteger(SectionWindow, KeyHeight, Height);
    }
}

void __fastcall THttpThreadForm::FormClose(TObject *Sender, TCloseAction &Action)
{
    std::unique_ptr<TIcsIniFile> IniFile(new TIcsIniFile(FIniFileName, true));
    IniFile->WriteString(SectionData, KeyURL, URLEdit->Text);
    IniFile->WriteString(SectionData, KeyProxy, ProxyEdit->Text);
    IniFile->WriteInteger(SectionWindow, KeyTop, Top);
    IniFile->WriteInteger(SectionWindow, KeyLeft, Left);
    IniFile->WriteInteger(SectionWindow, KeyWidth, Width);
    IniFile->WriteInteger(SectionWindow, KeyHeight, Height);
    IniFile->UpdateFile();
}

// Creates the threads on first use, and sends every idle one off to fetch the URL.
void __fastcall THttpThreadForm::DoItButtonClick(TObject *Sender)
{
    int TotThreads = Icsatoi(EditTotal->Text);
    if (TotThreads == 0 || TotThreads > MaxThreads)
        TotThreads = 1;
    for (int I = 0; I < TotThreads; I++) {
        THTTPThread *Thread = FThreads[I];
        if (Thread == nullptr) {
            Thread = new THTTPThread(I);
            // State is only read and written from the main thread.
            Thread->State = tsReady;
            DisplayThreadState(I, tsReady);
            Thread->Start();
            FThreads[I] = Thread;
        }

        if (Thread->State == tsReady) {
            // URL and Proxy may only be written while the thread is in state tsReady.
            Thread->URL = URLEdit->Text;
            Thread->Proxy = ProxyEdit->Text;
            Thread->State = tsBusy;
            DisplayThreadState(I, tsBusy);
            Thread->Wakeup();   // signals the event the thread's Execute waits on
        }
    }
}

void THttpThreadForm::DisplayThreadState(int Which, TThreadState State)
{
    TLabel *const StateLabels[MaxThreads] = {Thread0Label, Thread1Label, Thread2Label,
                                              Thread3Label, Thread4Label, Thread5Label};
    if (Which >= 0 && Which < MaxThreads)
        StateLabels[Which]->Caption = ThreadStateLits[State];
}

// A thread says its log list has lines; the thread object stays valid until the form frees it.
void __fastcall THttpThreadForm::WmLog(TMessage &Msg)
{
    if (FTerminating)
        return;
    THTTPThread *Thread = reinterpret_cast<THTTPThread *>(Msg.WParam);
    TLogListLock Lock(Thread);
    ResultsMemo->Lines->Add(Trim(Lock.List()->Text));
    Lock.List()->Clear();
}

// A thread says its GET has finished; LParam is the success flag.
void __fastcall THttpThreadForm::WmThreadResult(TMessage &Msg)
{
    if (!FTerminating)
        ProcessResults(reinterpret_cast<THTTPThread *>(Msg.WParam), Msg.LParam != 0);
}

void THttpThreadForm::ProcessResults(THTTPThread *AThread, bool Success)
{
    AThread->State = tsReady;
    DisplayThreadState(AThread->ThreadNumber, AThread->State);
    // Safe to read the thread's fields: it is idle, waiting for its event.
    ResultsMemo->Lines->Add("* * * * * * THREAD " + IntToStr(AThread->ThreadNumber) + " * * * * * *");
    ResultsMemo->Lines->Add("Status Code: " + IntToStr(AThread->StatusCode));
    if (Success) {
        ResultsMemo->Lines->Add(AThread->BodyContent);
        ResultsMemo->Lines->Add("");
    }
    else
        ResultsMemo->Lines->Add("Nothing returned by thread");
    ResultsMemo->Lines->Add("* * * * * * * * * * * * * * * * * * * *");
}
