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

  Each THTTPThread creates its own TSslHttpRest, waits for an event, does one synchronous GET
  with the component's internal message pump, and posts the result to the main form.
  The URL, Proxy and State properties are only touched by the main thread while the thread is
  idle (State is tsReady).

  How this C++ version differs from the Delphi original, apart from syntax:
  - The component is a std::unique_ptr local in Execute (Delphi: Create and a finally Free).
  - The destructor waits for the thread itself, because the members the thread uses are
    destroyed before the TThread base destructor would wait.
*/

#include <vcl.h>
#pragma hdrstop

#include "OverbyteIcsHttpThr2.h"
#include "OverbyteIcsHttpThr1.h"

#include "OverbyteIcsSslHttpRest.hpp"
#include "OverbyteIcsUtils.hpp"

#pragma package(smart_init)

const wchar_t *const ThreadStateLits[3] = {L"tsInvalid", L"tsReady", L"tsBusy"};

namespace {

// Holds a critical section for the length of a scope.
class TCritSectLock
{
public:
    explicit TCritSectLock(CRITICAL_SECTION &CritSect) : FCritSect(CritSect) { EnterCriticalSection(&FCritSect); }
    ~TCritSectLock() { LeaveCriticalSection(&FCritSect); }
private:
    CRITICAL_SECTION &FCritSect;
};

}  // namespace

__fastcall THTTPThread::THTTPThread(int AThreadNumber)
    : TThread(true), FEvent(nullptr), FState(tsInvalid), FThreadNumber(AThreadNumber),
      FStatusCode(0), FSuccess(false), FLogList(new TStringList)
{
    FEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);   // auto-reset, not signalled
    if (FEvent == nullptr)
        RaiseLastOSError();
    InitializeCriticalSection(&FCritSect);
}

__fastcall THTTPThread::~THTTPThread()
{
    Terminate();
    SetEvent(FEvent);   // wake it up, otherwise WaitFor never returns
    WaitFor();
    DeleteCriticalSection(&FCritSect);
    CloseHandle(FEvent);
}

void THTTPThread::Wakeup()
{
    SetEvent(FEvent);
}

TStringList *THTTPThread::LockLogList()
{
    EnterCriticalSection(&FCritSect);
    return FLogList.get();
}

void THTTPThread::UnlockLogList()
{
    LeaveCriticalSection(&FCritSect);
}

// Queues a line for the main form; only the first line after the form emptied the list posts a message.
void THTTPThread::Progress(const String &Msg)
{
    TCritSectLock Lock(FCritSect);
    FLogList->Add(Msg);
    if (FLogList->Count == 1)
        PostMessage(HttpThreadForm->Handle, WM_LOG, reinterpret_cast<WPARAM>(this), 0);
}

void __fastcall THTTPThread::Execute()
{
    IcsNameThreadForDebugging(AnsiString(ClassName() + " " + IntToStr(FThreadNumber)));
    std::unique_ptr<TSslHttpRest> SslHttpRest(new TSslHttpRest(nullptr));
    SslHttpRest->OnHttpRestProg = OnSslHttpRestProg;
    SslHttpRest->MultiThreaded = true;
    while (!Terminated) {
        // Wait until the main form wakes the thread for a new request.
        WaitForSingleObject(FEvent, INFINITE);
        if (Terminated)
            break;

        Progress("Thread " + IntToStr(FThreadNumber) + ": Start GET");
        SslHttpRest->Proxy = FProxy;
        FSuccess = false;
        FStatusCode = 0;   // Delphi kept the previous request's status code when this request raised
        try {
            SslHttpRest->RestRequest(httpGET, FURL);   // synchronous, with the component's internal message pump
            FStatusCode = SslHttpRest->StatusCode;
            if (FStatusCode == 200) {
                if (SslHttpRest->ContentType.Pos("text/") > 0)
                    FBodyContent = SslHttpRest->ResponseRaw;
                else
                    FBodyContent = "<Non-textual content received: " + SslHttpRest->ContentType + ">";
                FSuccess = true;
            }
        }
        catch (Exception &) {
            FSuccess = false;   // the form reports "Nothing returned by thread"
        }
        SslHttpRest->Close();

        if (!Terminated)
            PostMessage(HttpThreadForm->Handle, WM_THREAD_RESULT, reinterpret_cast<WPARAM>(this), FSuccess ? 1 : 0);
    }
}

void __fastcall THTTPThread::OnSslHttpRestProg(TObject *Sender, TLogOption LogOption, const String Msg)
{
    Progress("Thread " + IntToStr(FThreadNumber) + ": " + Msg);
}
