#ifndef OverbyteIcsBinCliDemo1H
#define OverbyteIcsBinCliDemo1H
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.ExtCtrls.hpp>
#include <Vcl.StdCtrls.hpp>

#include <bitset>
#include <memory>

#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>

#include "OverbyteIcsTypes.hpp"
#include "OverbyteIcsWSocket.hpp"

struct THdrRec
{
    int ID;
    int Size;
};

enum TBinStatus { stBinReady, stBinHdrReceived };
enum TReceiveMode { rmLine, rmBinary };

typedef void __fastcall (__closure *TBinaryReceivedEvent)(TObject *Sender, const void *Buf, int Size);
typedef void __fastcall (__closure *TBinaryBeginEvent)(TObject *Sender, const THdrRec &Header);
typedef void __fastcall (__closure *TLineReceivedEvent)(TObject *Sender, const String Line);

class TBinaryWSocket : public TWSocket
{
private:
    TBinStatus FBinStatus = stBinReady;
    int FDataLen = 0;
    int FReadOffs = 0;
    int FCurrentBlockSize = 0;
    TReceiveMode FReceiveMode = rmLine;
    unsigned char FRcvBuf[4096];
    int FWriteOffs = 0;
    int FRemainingBytes = 0;
    TBinaryBeginEvent FOnBinaryBegin = nullptr;
    TNotifyEvent FOnBinaryEnd = nullptr;
    TBinaryReceivedEvent FOnBinaryReceived = nullptr;
    TLineReceivedEvent FOnLineReceived = nullptr;
    std::bitset<256> FTextLineIDs;

    void __fastcall ParseReceiveBuffer();
    void __fastcall TriggerBinaryReceived(const void *Buf, int Size);
    void __fastcall TriggerBinaryBegin(const THdrRec &Header);
    void __fastcall TriggerBinaryEnd();
    void __fastcall TriggerLineReceived(const char *Buf);

protected:
    virtual void __fastcall AssignDefaultValue() override;
    virtual bool __fastcall TriggerDataAvailable(System::Word ErrCode) override;

public:
    __fastcall TBinaryWSocket(TComponent *AOwner) override;
    __property int CurrentBlockSize = {read=FCurrentBlockSize};
    __property TReceiveMode ReceiveMode = {read=FReceiveMode, write=FReceiveMode};
    __property int RemainingBytes = {read=FRemainingBytes};
    __property TBinaryBeginEvent OnBinaryBegin = {read=FOnBinaryBegin, write=FOnBinaryBegin};
    __property TNotifyEvent OnBinaryEnd = {read=FOnBinaryEnd, write=FOnBinaryEnd};
    __property TBinaryReceivedEvent OnBinaryReceived = {read=FOnBinaryReceived, write=FOnBinaryReceived};
    __property TLineReceivedEvent OnLineReceived = {read=FOnLineReceived, write=FOnLineReceived};
    std::bitset<256> &TextLineIDs() { return FTextLineIDs; }
};

class TBinClientForm : public TForm
{
__published:
    TMemo *DisplayMemo;
    TPanel *Panel1;
    TLabel *Label1;
    TLabel *Label2;
    TEdit *SendEdit;
    TButton *SendButton;
    TButton *DisconnectButton;
    TEdit *PortEdit;
    TEdit *ServerEdit;
    TLabel *Label3;
    TCheckBox *AddCRLFCheckBox;
    void __fastcall DisconnectButtonClick(TObject *Sender);
    void __fastcall SendButtonClick(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
    void __fastcall FormShow(TObject *Sender);
    void __fastcall FormCreate(TObject *Sender);

private:
    std::unique_ptr<TBinaryWSocket> CliSocket;
    bool FInitialized = false;
    String FIniFileName;

    void __fastcall CliSocketSessionConnected(TObject *Sender, System::Word ErrCode);
    void __fastcall CliSocketSessionClosed(TObject *Sender, System::Word ErrCode);
    void __fastcall CliSocketBinaryReceived(TObject *Sender, const void *Buf, int Size);
    void __fastcall CliSocketBinaryBegin(TObject *Sender, const THdrRec &Header);
    void __fastcall CliSocketBinaryEnd(TObject *Sender);
    void __fastcall CliSocketLineReceived(TObject *Sender, const String Line);
    void __fastcall Display(const String &Msg);
    void __fastcall SendData();
    void __fastcall ProcessCommand(TObject *Sender, const String &Str);

public:
    __fastcall TBinClientForm(TComponent *Owner);
};

extern PACKAGE TBinClientForm *BinClientForm;

#endif
