/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program 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.

This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef hsStream_Defined
#define hsStream_Defined

#include <stdarg.h> // Included for GCC 3.2.2+

#include "HeadSpin.h"
#include "hsMemory.h"
#include "plString.h"

namespace hsPackFileSys {
struct FileEntry;
}

// Define this for use of Streams with Logging (commonly used w/ a packet sniffer)
// These streams log their reads to an event list
//#define STREAM_LOGGER

#ifndef STREAM_LOGGER
#define hsReadOnlyLoggingStream hsReadOnlyStream
#define LogRead(byteCount, buffer, desc) Read(byteCount, buffer)
#define LogReadSafeString() ReadSafeString()
#define LogReadSafeString_TEMP() ReadSafeString_TEMP()
#define LogReadSafeStringLong() ReadSafeStringLong();
#define LogSkip(deltaByteCount, desc) Skip(deltaByteCount)
#define LogReadLE(value, desc) ReadLE(value)
#define LogReadLEArray(count, values, desc) ReadLE(count, values)
#define LogSubStreamStart(desc) LogVoidFunc()
#define LogSubStreamPushDesc(desc) LogVoidFunc()
#define LogSubStreamEnd() LogVoidFunc()
#define LogStringString(s) LogVoidFunc()
#endif

class hsStream {
public:
enum {
    kEolnCode = '\n',
    kComment = '#'
    };
enum  VDB_Type  {// Virtual Database type
    kVDB_GroupObject,
    kVDB_Mesh
    };
protected:
    uint32_t      fBytesRead;
    uint32_t      fPosition;

    hsBool      IsTokenSeparator(char c);
public:
                hsStream() : fBytesRead(0), fPosition(0) {}
    virtual     ~hsStream();

    virtual hsBool  Open(const char *, const char * = "rb")=0;
    virtual hsBool  Open(const wchar_t *, const wchar_t * = L"rb")=0;
    virtual hsBool  Close()=0;
    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void * buffer) = 0;
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer) = 0;
    virtual void    Skip(uint32_t deltaByteCount) = 0;
    virtual void    Rewind() = 0;
    virtual void    FastFwd();
    virtual uint32_t  GetPosition() const;
    virtual void    SetPosition(uint32_t position);
    virtual void    Truncate();
    virtual void    Flush() {}

#ifdef STREAM_LOGGER
    // Logging Reads & Skips
    virtual uint32_t  LogRead(uint32_t byteCount, void * buffer, const char* desc) { return Read(byteCount,buffer); }
    virtual char*   LogReadSafeString() { return ReadSafeString(); }
    virtual char*   LogReadSafeStringLong() { return ReadSafeStringLong(); }
    virtual void    LogSkip(uint32_t deltaByteCount, const char* desc) { Skip(deltaByteCount); }

    // Stream Notes for Logging 
    virtual void    LogStringString(const char* s) { }
    virtual void    LogSubStreamStart(const char* desc) { }
    virtual void    LogSubStreamEnd() { }
    virtual void    LogSubStreamPushDesc(const char* desc) { }
#endif
    void LogVoidFunc() { }

    // Optimization for small Reads
    virtual uint8_t ReadByte();
    virtual hsBool  Read4Bytes(void *buffer);   // Reads 4 bytes,  return true if success 
    virtual hsBool  Read8Bytes(void *buffer);   // Reads 8 bytes,  return true if success 
    virtual hsBool  Read12Bytes(void *buffer);  // Reads 12 bytes, return true if success

    virtual uint32_t  GetEOF();
    uint32_t          GetSizeLeft();
    virtual void      CopyToMem(void* mem);
    virtual hsBool    IsCompressed() { return false; }

    uint32_t        WriteString(const char cstring[]);
    uint32_t        WriteString(const plString & string) { return WriteString(_TEMP_CONVERT_TO_CONST_CHAR(string)); }
    uint32_t        WriteFmt(const char * fmt, ...);
    uint32_t        WriteFmtV(const char * fmt, va_list av);

    uint32_t        WriteSafeStringLong(const char *string);    // uses 4 bytes for length
    uint32_t        WriteSafeWStringLong(const wchar_t *string);
    char *          ReadSafeStringLong();
    wchar_t *       ReadSafeWStringLong();

    uint32_t        WriteSafeString(const char *string);        // uses 2 bytes for length
    uint32_t        WriteSafeWString(const wchar_t *string);
    char *          ReadSafeString();
    wchar_t *       ReadSafeWString();

    uint32_t        WriteSafeString(const plString &string);        // uses 2 bytes for length
    uint32_t        WriteSafeWString(const plString &string);
    plString        ReadSafeString_TEMP();
    plString        ReadSafeWString_TEMP();

    hsBool          GetToken(char *s, uint32_t maxLen=uint32_t(-1), const char beginComment=kComment, const char endComment=kEolnCode);
    hsBool          ReadLn(char* s, uint32_t maxLen=uint32_t(-1), const char beginComment=kComment, const char endComment=kEolnCode);
    
    bool            Readbool();
    hsBool          ReadBool();
    void            ReadBool(int count, hsBool values[]);
    uint16_t        ReadLE16();
    void            ReadLE16(int count, uint16_t values[]);
    uint32_t        ReadLE32();
    void            ReadLE32(int count, uint32_t values[]);
    uint32_t        ReadBE32();

    void            Writebool(bool value);
    void            WriteBool(hsBool value);
    void            WriteBool(int count, const hsBool values[]);
    void            WriteByte(uint8_t value);
    void            WriteLE16(uint16_t value);
    void            WriteLE16(int count, const uint16_t values[]);
    void            WriteLE32(uint32_t value);
    void            WriteLE32(int count, const  uint32_t values[]);
    void            WriteBE32(uint32_t value);


    /* Overloaded  Begin (8 & 16 & 32 int)*/
    /* yes, swapping an 8 bit value does nothing, just useful*/
    void            ReadLE(bool* value) { *value = this->ReadByte() ? true : false; }
    void            ReadLE(uint8_t* value) { *value = this->ReadByte(); }
    void            ReadLE(int count, uint8_t values[]) { this->Read(count, values); }
    void            ReadLE(uint16_t* value) { *value = this->ReadLE16(); }
    void            ReadLE(int count, uint16_t values[]) { this->ReadLE16(count, values); }
    void            ReadLE(uint32_t* value) { *value = this->ReadLE32(); }
    void            ReadLE(int count, uint32_t values[]) { this->ReadLE32(count, values); }
#ifdef STREAM_LOGGER
                // Begin LogReadLEs
    virtual void    LogReadLE(bool* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLE(uint8_t* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, uint8_t values[], const char* desc) { this->ReadLE(count, values); }
    virtual void    LogReadLE(uint16_t* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, uint16_t values[], const char* desc) { this->ReadLE(count, values); }
    virtual void    LogReadLE(uint32_t* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, uint32_t values[], const char* desc) { this->ReadLE(count, values); }
                // End LogReadLEs
#endif
    void            WriteLE(bool value) { this->Write(1,&value); }
    void            WriteLE(uint8_t value) { this->Write(1,&value); }
    void            WriteLE(int count, const uint8_t values[]) { this->Write(count, values); }
    void            WriteLE(uint16_t value) { this->WriteLE16(value); }
    void            WriteLE(int count, const uint16_t values[]) { this->WriteLE16(count, values); }
    void            WriteLE(uint32_t value) { this->WriteLE32(value); }
    void            WriteLE(int count, const  uint32_t values[]) { this->WriteLE32(count, values); }
    void            ReadLE(int8_t* value) { *value = this->ReadByte(); }
    void            ReadLE(int count, int8_t values[]) { this->Read(count, values); }
    void            ReadLE(char* value) { *value = (char)this->ReadByte(); }
    void            ReadLE(int count, char values[]) { this->Read(count, values); }
    void            ReadLE(int16_t* value) { *value = (int16_t)this->ReadLE16(); }
    void            ReadLE(int count, int16_t values[]) { this->ReadLE16(count, (uint16_t*)values); }
    void            ReadLE(int32_t* value) { *value = (int32_t)this->ReadLE32(); }
    void            ReadLE(int count, int32_t values[]) { this->ReadLE32(count, (uint32_t*)values); }
#ifdef STREAM_LOGGER
                // Begin LogReadLEs
    virtual void    LogReadLE(int8_t* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, int8_t values[], const char* desc) { this->ReadLE(count, values); }
    virtual void    LogReadLE(char* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, char values[], const char* desc) { this->ReadLE(count, values); }
    virtual void    LogReadLE(int16_t* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, int16_t values[], const char* desc) { this->ReadLE(count, (uint16_t*)values); }
    virtual void    LogReadLE(int32_t* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, int32_t values[], const char* desc) { this->ReadLE(count, (uint32_t*)values); }
    virtual void    LogReadLE(int* value, const char* desc) { this->ReadLE(value); }
    virtual void    LogReadLEArray(int count, int values[], const char* desc) { this->ReadLE(count, (uint32_t*)values); }
                // End LogReadLEs
#endif
    void            WriteLE(int8_t value) { this->Write(1,&value); }
    void            WriteLE(int count, const int8_t values[]) { this->Write(count, values); }
    void            WriteLE(char value) { this->Write(1,(uint8_t*)&value); }
    void            WriteLE(int count, const char values[]) { this->Write(count, (uint8_t*)values); }
    void            WriteLE(int16_t value) { this->WriteLE16((uint16_t)value); }
    void            WriteLE(int count, const int16_t values[]) { this->WriteLE16(count, (uint16_t*)values); }
    void            WriteLE(int32_t value) { this->WriteLE32((uint32_t)value); }
    void            WriteLE(int count, const  int32_t values[]) { this->WriteLE32(count, (uint32_t*)values); }
    /* Overloaded  End */


    float           ReadLEFloat();
    void            ReadLEFloat(int count, float values[]);
    double          ReadLEDouble();
    void            ReadLEDouble(int count, double values[]);
    float           ReadBEFloat();
    void            WriteLEFloat(float value);
    void            WriteLEFloat(int count, const float values[]);
    void            WriteLEDouble(double value);
    void            WriteLEDouble(int count, const double values[]);
    void            WriteBEFloat(float value);


    /* Overloaded  Begin (Float)*/
    void            ReadLE(float* value) { *value = ReadLEFloat(); }
    void            ReadLE(int count, float values[]) { ReadLEFloat(count, values); }
    void            ReadLE(double* value) { *value = ReadLEDouble(); }
    void            ReadLE(int count, double values[]) { ReadLEDouble(count, values); }
#ifdef STREAM_LOGGER
                    // Begin LogReadLEs
    virtual void    LogReadLE(float* value, const char* desc) { ReadLE(value); }
    virtual void    LogReadLEArray(int count, float values[], const char* desc) { ReadLE(count, values); }
    virtual void    LogReadLE(double* value, const char* desc) { ReadLE(value); }
    virtual void    LogReadLEArray(int count, double values[], const char* desc) { ReadLE(count, values); }
                    // End LogReadLEs
#endif
    void            WriteLE(float value) { WriteLEFloat(value); }
    void            WriteLE(int count, const float values[]) { WriteLEFloat(count, values); }
    void            WriteLE(double value) { WriteLEDouble(value); }
    void            WriteLE(int count, const double values[]) { WriteLEDouble(count, values); }
    /* Overloaded End */

    float           ReadLEScalar() { return (float)this->ReadLEFloat(); }
    void            ReadLEScalar(int count, float values[])
                    {
                        this->ReadLEFloat(count, (float*)values);
                    }
    float           ReadBEScalar() { return (float)this->ReadBEFloat(); }
    void            WriteLEScalar(float value) { this->WriteLEFloat(value); }
    void            WriteLEScalar(int count, const float values[])
                    {
                        this->WriteLEFloat(count, (float*)values);
                    }
    void            WriteBEScalar(float value) { this->WriteBEFloat(value); }

    void            WriteLEAtom(uint32_t tag, uint32_t size);
    uint32_t          ReadLEAtom(uint32_t* size);


    /* Overloaded  Begin (Atom)*/
    void            WriteLE(uint32_t* tag, uint32_t size) { WriteLEAtom(*tag, size); }
    void            ReadLE(uint32_t* tag, uint32_t *size) { *tag = ReadLEAtom(size); }
    /* Overloaded  End */
    virtual void    VirtualSetPosition(uint32_t pos, VDB_Type ){ SetPosition(pos); };
    virtual hsPackFileSys::FileEntry *GetFileEntry() { return nil; }  // Streams from Packfiles can return a FileEntry

};

class hsStreamable {
public:
    virtual void    Read(hsStream* stream) = 0;
    virtual void    Write(hsStream* stream) = 0;
    virtual uint32_t  GetStreamSize() = 0;
};

class hsFileStream: public hsStream
{   
    uint32_t      fRef;

public:
                hsFileStream();
    virtual     ~hsFileStream();
    virtual hsBool  Open(const char *name, const char *mode = "rb");
    virtual hsBool  Open(const wchar_t *name, const wchar_t *mode = L"rb");
    virtual hsBool  Close();

    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void* buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    Truncate();

    virtual uint32_t  GetFileRef();
    virtual void    SetFileRef(uint32_t refNum);
};

class hsUNIXStream: public hsStream
{   
    FILE*       fRef;
    char*       fBuff;

public:
    hsUNIXStream(): fRef(0), fBuff(nil) {}
    ~hsUNIXStream();
    virtual hsBool  Open(const char* name, const char* mode = "rb");
    virtual hsBool  Open(const wchar_t *name, const wchar_t *mode = L"rb");
    virtual hsBool  Close();

    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void* buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    SetPosition(uint32_t position);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    FastFwd();
    virtual void    Truncate();
    virtual void    Flush();

    FILE*           GetFILE() { return fRef; }
    void            SetFILE(FILE* file) { fRef = file; }

    virtual uint32_t  GetEOF();
};

// Small substream class: give it a base stream, an offset and a length, and it'll
// treat all ops as if you had a chunk from the base stream as a separate, vanilla 
// stream of the given length.

class plReadOnlySubStream: public hsStream
{   
    hsStream    *fBase;
    uint32_t      fOffset, fLength;

    void    IFixPosition( void );

public:
    plReadOnlySubStream(): fBase( nil ), fOffset( 0 ), fLength( 0 ) {}
    ~plReadOnlySubStream();

    virtual hsBool  Open(const char *, const char *)    { hsAssert(0, "plReadOnlySubStream::Open  NotImplemented"); return false; }
    virtual hsBool  Open(const wchar_t *, const wchar_t *)  { hsAssert(0, "plReadOnlySubStream::Open  NotImplemented"); return false; }
    void    Open( hsStream *base, uint32_t offset, uint32_t length );
    virtual hsBool  Close() { fBase = nil; fOffset = 0; fLength = 0; return true; }
    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void* buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    FastFwd();
    virtual void    Truncate();

    virtual uint32_t  GetEOF();
};

class hsRAMStream : public hsStream {
    hsAppender          fAppender;
    hsAppenderIterator  fIter;
public:
                hsRAMStream();
                hsRAMStream(uint32_t chunkSize);
    virtual     ~hsRAMStream();

    virtual hsBool  Open(const char *, const char *)    { hsAssert(0, "hsRAMStream::Open  NotImplemented"); return false; }
    virtual hsBool  Open(const wchar_t *, const wchar_t *)  { hsAssert(0, "hsRAMStream::Open  NotImplemented"); return false; }
    virtual hsBool  Close()             { hsAssert(0, "hsRAMStream::Close  NotImplemented"); return false; }

    
    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void * buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    Truncate();

    virtual uint32_t  GetEOF();
    virtual void    CopyToMem(void* mem);

    void            Reset();        // clears the buffers
};

class hsNullStream : public hsStream {
public:

    virtual hsBool  Open(const char *, const char *)    { return true; }
    virtual hsBool  Open(const wchar_t *, const wchar_t *)  { return true; }
    virtual hsBool  Close()             { return true; }

    virtual uint32_t  Read(uint32_t byteCount, void * buffer);  // throw's exception
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    Truncate();

    uint32_t      GetBytesWritten() const { return fBytesRead; }
    void            Reset( ) { fBytesRead = 0;   }
};

// read only mem stream
class hsReadOnlyStream : public hsStream {
protected:
    char*   fStart;
    char*   fData;
    char*   fStop;
public:
    hsReadOnlyStream(int size, const void* data) { Init(size, data); }
    hsReadOnlyStream() {}

    virtual void    Init(int size, const void* data) { fStart=((char*)data); fData=((char*)data); fStop=((char*)data + size); }
    virtual hsBool  Open(const char *, const char *)    { hsAssert(0, "hsReadOnlyStream::Open  NotImplemented"); return false; }
    virtual hsBool  Open(const wchar_t *, const wchar_t *)  { hsAssert(0, "hsReadOnlyStream::Open  NotImplemented"); return false; }
    virtual hsBool  Close()             { hsAssert(0, "hsReadOnlyStream::Close  NotImplemented"); return false; }
    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void * buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);    // throws exception
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    Truncate();
    virtual uint32_t  GetBytesRead() const { return fBytesRead; }
    virtual uint32_t  GetEOF() { return (uint32_t)(fStop-fStart); }
    virtual void    CopyToMem(void* mem);
};

// write only mem stream
class hsWriteOnlyStream : public hsReadOnlyStream {
public:
    hsWriteOnlyStream(int size, const void* data) : hsReadOnlyStream(size, data) {}
    hsWriteOnlyStream() {}

    virtual hsBool  Open(const char *, const char *)    { hsAssert(0, "hsWriteOnlyStream::Open  NotImplemented"); return false; }
    virtual hsBool  Open(const wchar_t *, const wchar_t *)  { hsAssert(0, "hsWriteOnlyStream::Open  NotImplemented"); return false; }
    virtual hsBool  Close()             { hsAssert(0, "hsWriteOnlyStream::Close  NotImplemented"); return false; }
    virtual uint32_t  Read(uint32_t byteCount, void * buffer);  // throws exception
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);    
    virtual uint32_t  GetBytesRead() const { return 0; }
    virtual uint32_t  GetBytesWritten() const { return fBytesRead; }
};

// circular queue stream
class hsQueueStream : public hsStream {
private:
    char* fQueue;
    uint32_t fReadCursor;
    uint32_t fWriteCursor;
    uint32_t fSize;
    
public:
    hsQueueStream(int32_t size);
    ~hsQueueStream();

    virtual hsBool  Open(const char *, const char *)    { hsAssert(0, "hsQueueStream::Open  NotImplemented"); return false; }
    virtual hsBool  Open(const wchar_t *, const wchar_t *)  { hsAssert(0, "hsQueueStream::Open  NotImplemented"); return false; }
    virtual hsBool  Close()             { hsAssert(0, "hsQueueStream::Close  NotImplemented"); return false; }

    virtual uint32_t  Read(uint32_t byteCount, void * buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    FastFwd();
    virtual hsBool  AtEnd();

    uint32_t GetSize() { return fSize; }
    const char* GetQueue() { return fQueue; }
    uint32_t GetReadCursor() { return fReadCursor; }
    uint32_t GetWriteCursor() { return fWriteCursor; }
};

class hsBufferedStream : public hsStream
{
    FILE* fRef;
    uint32_t fFileSize;

    enum { kBufferSize = 2*1024 };
    char fBuffer[kBufferSize];
    // If the buffer is empty, this is zero.  Otherwise it is the size of the
    // buffer (if we read a full block), or something less than that if we read
    // a partial block at the end of the file.
    uint32_t fBufferLen;

    hsBool fWriteBufferUsed;

#ifdef HS_DEBUGGING
    // For doing statistics on how efficient we are
    int fBufferHits, fBufferMisses;
    uint32_t fBufferReadIn, fBufferReadOut, fReadDirect, fLastReadPos;
    char* fFilename;
    const char* fCloseReason;
#endif

public:
    hsBufferedStream();
    virtual ~hsBufferedStream();

    virtual hsBool  Open(const char* name, const char* mode = "rb");
    virtual hsBool  Open(const wchar_t* name, const wchar_t* mode = L"rb");
    virtual hsBool  Close();

    virtual hsBool  AtEnd();
    virtual uint32_t  Read(uint32_t byteCount, void* buffer);
    virtual uint32_t  Write(uint32_t byteCount, const void* buffer);
    virtual void    Skip(uint32_t deltaByteCount);
    virtual void    Rewind();
    virtual void    Truncate();
    virtual uint32_t  GetEOF();

    FILE*   GetFileRef();
    void    SetFileRef(FILE* file);

    // Something optional for when we're doing stats.  Will log the reason why
    // the file was closed.  Really just for plRegistryPageNode.
    void SetCloseReason(const char* reason)
    {
#ifdef HS_DEBUGGING
        fCloseReason = reason;
#endif
    }
};

#endif