#if !defined(__MUGSOCS__)
#define __MUGSOCS__

#define CSOC_SERVER_ID ((UINT)INT_MIN)
#define CSOC_CLIENT_ID ((UINT)0)
#define CSOC_LISTEN_ID ((UINT)-1)

#if !defined(STATIC_FDARRAY_MAX)
#define STATIC_FDARRAY_MAX 4
#endif // !defined(STATIC_FDARRAY_MAX)
#if STATIC_FDARRAY_MAX < 0
  #error must : STATIC_FDARRAY_MAX >= 0
#endif

#if defined(SR_ASSERT)
  #ifdef ASSERT
    #undef ASSERT
  #endif // ASSERT
  #define ASSERT SR_ASSERT
#endif // SR_ASSERT

#ifndef NULL_CLASS_DEFINED
#define NULL_CLASS_DEFINED
class CNullClass {};
#endif

#if !defined(SOCBase_Class)
#define SOCBase_Class CNullClass
#endif // !defined(SOCBase_Class)

extern char SERVERID[];

#ifdef WIN32
#define ioctl(a,b,c) ioctlsocket(a,b,c)
#endif

//#define CMD_TYPEBREAKER 128

#ifndef MAX_PACKET
#define MAX_PACKET      2048
#endif // !MAX_PACKET
#ifndef MAX_FTPBUF
#define MAX_FTPBUF      1024
// MAX_FTPBUF must be smaller than MAX_PACKET?
// 9+size==sizeof(bCmd) + sizeof(FTID) + sizeof(size) + size
#endif // !MAX_FTPBUF

// #define NO_PDCD - define PDCD for - no check PACKET_DUMP_CHECK_DISASSEMBLE - be very cautious!!!
// #define NO_PDCA - define PDCA for - no check PACKET_DUMP_CHECK_ASSEMBLE - be very cautious???

#ifndef MY_LINGER_TIME
#define MY_LINGER_TIME 250
#endif // !MY_LINGER_TIME

#ifndef RCV_MAX
#define RCV_MAX         10240
#endif // !RCV_MAX

#ifndef SND_MAX
#define SND_MAX         10240
#endif // !SND_MAX

#if (SND_MAX < MAX_PACKET) || (RCV_MAX < MAX_PACKET)
#error Both(SND_MAX & RCV_MAX) >= MAX_PACKET
#endif

#ifdef WIN32
  #if !defined(NO_USE_OVRIO)
    #define USE_OVRIO
  #endif // !defined(NO_USE_OVRIO)
#else // !WIN32
  #undef USE_OVRIO
#endif // WIN32
//#undef USE_OVRIO

#define PUSHFLOAT(f)  (*(DWORD*)&(f))

enum EException
{
    EEXCP_NoError,

    // local side...
    EEXCP_TooLowVersion,
    EEXCP_Unknown,
    EEXCP_ConnectionTimeOut,
    EEXCP_ConnectionFailure,
    EEXCP_SendBufferOverflow,
    EEXCP_VersionError,

    // fatal socket error...
    EEXCP_WSAENETDOWN,
    EEXCP_WSANO_RECOVERY,
    EEXCP_WSAENETRESET,
    EEXCP_WSAECONNRESET,
    EEXCP_WSAECONNABORTED,
    EEXCP_WSAEADDRINUSE,
    EEXCP_WSAETIMEDOUT,
    EEXCP_WSAEHOSTUNREACH,
    EEXCP_WSAEACCES,

    // internal error
    EEXCP_WSANOTINITIALISED,
    EEXCP_WSAEFAULT,
    EEXCP_WSAENOTSOCK,
    EEXCP_WSAENOTCONN,
    EEXCP_WSAEOPNOTSUPP,
    EEXCP_WSAEINVAL,
    EEXCP_DupCreation,
};

#if !defined(STRF_MAX_PARAMS)
#define STRF_MAX_PARAMS  7 // should be 3 or 7
#endif
#if !((STRF_MAX_PARAMS==3) || (STRF_MAX_PARAMS==7))
 #error STRF_MAX_PARAMS size must be 3 or 7...
#endif

const char* SkipWord(const char* pcsz);
const char* SkipWhite(const char* pcsz);
const char* SkipWhiteReverse(const char* pcsz);
BOOL IsControlExist(const char* pcsz);
BOOL IsAllDigit(const char* pcsz);
BOOL CheckStringOK(const char* pcsz, int nMin, int nMax);
BOOL CheckNumberOK(const char* pcsz, int nMin, int nMax);
BOOL IsInvalidCharsExist(const char* pcsz, const char* pcszInvalidChars);
char* cmd_GetString(char*& psz, int nQuoteType = 0);
int cmd_GetNumber(char*& psz, int& nNumber, int nMin = 0, int nMax = 65536);

#ifdef WIN32
#pragma pack(push, 1)
#endif // WIN32
DECLARE_TARRAYREF(CTEXTFmt);

class CTEXTFmt : public CStr
{
public:
    union {
    struct {
    WORD m_wFmt;
    WORD m_wTX;
    } m_DW;
    DWORD m_dw;
    };
    char m_caParamType[STRF_MAX_PARAMS + 1];
public:
    CTEXTFmt(const char* pcszFormat = NULL);
};
#ifdef WIN32
#pragma pack(pop)
#endif // WIN32

class CTEXTFmtArray : public TARRAYREF(CTEXTFmt)
{
public:
    class CAtoms : public TARRAYREF(CStr)
    {
    public:
        CStr m_strTitle;
    public:
        CAtoms(const char* pcsz)
            : m_strTitle(pcsz)
        {
            SetGrowBy(10);
        }
    };
    TARRAYPTR(CAtoms*) m_paAtoms;
public:
    CTEXTFmtArray();
    virtual ~CTEXTFmtArray();
    int LoadFmts(const char* pcszFile, const char* pcszSignature, BOOL bCheckACP = FALSE);
    CTEXTFmt& FindTEXTFmt(WORD wFmt) const;
    CAtoms* FindAtoms(const char* pcsz) const;
    void Sort();
private:
    int ProcessOneLine(BYTE baLine[], const char* pcszSignature, int nSizeOld, int nSize);
    CTEXTFmt& FindOnLoad(WORD wFmt, int nSizeOld, int nSize) const;
    static void ProcessEscape(BYTE* pb);
};

#define ESTRF_OK 0 // no error
#define ESTRF_ERR (-1)

// Login Error
#define ESTRF_VERSIONERROR 10
#define ESTRF_RESTART_PROGRAM 11
#define ESTRF_TOOMANY_CONNECTIONS 12
#define ESTRF_INVALIDLOGININFO 13 // C_LOGN_INFO invalid id or password
#define ESTRF_INVALIDLOGINNAME 14 // C_LOGN_INFO
#define ESTRF_INVALIDPASSWORD 15 // C_LOGN_INFO, C_ROOM_JOIN
#define ESTRF_ALREADYLOGIN 16 // C_LOGN_INFO
#define ESTRF_SAMEUSEREXIST 17 // C_USER_INFO
#define ESTRF_NOSUCHUSER 18 // C_USER_INFO
#define ESTRF_NAME 19 // C_USER_INFO
#define ESTRF_EMAIL 20 // C_USER_INFO
#define ESTRF_DATEBORN 21 // C_USER_INFO
#define ESTRF_HOMEPAGE 22 // C_USER_INFO

// DB error
#define ESTRF_DBCONNECTION 30 // C_LOGN_INFO
#define ESTRF_DBOPEN 31 // C_LOGN_INFO
#define ESTRF_DBRESTART 32 // C_LOGN_INFO
#define ESTRF_DBACCESS 33 // C_LOGN_INFO

// Room Error
#define ESTRF_TOOMANYROOMS 40 // C_ROOM_MAKE
#define ESTRF_SAMEROOMEXIST 41 // C_ROOM_MAKE
#define ESTRF_NOSUCHROOM 42 // C_ROOM_JOIN
#define ESTRF_ALREADYINROOM 43 // C_ROOM_JOIN
#define ESTRF_NOOTHERUSERS_IN_ROOM 44
#define ESTRF_TOOMANYUSERS_IN_ROOM 45 // C_ROOM_JOIN
#define ESTRF_INVALID_ROOMNAME 46 // C_ROOM_JOIN...
#define ESTRF_INVALID_ROOMPASSWORD 47 // C_ROOM_JOIN...
#define ESTRF_INVALID_MAX 48 // C_ROOM_MAKE
#define ESTRF_ROOM_STAT 49 // C_ROOM_STAT

// Plyr Error
#define ESTRF_OWNERCOMMAND 50
#define ESTRF_KICKEDBYOWNER 51
#define ESTRF_BANISHIEDBYOWNER 52
#define ESTRF_YOUAREALREADYBANISHIED 53
#define ESTRF_NOSUCHUSERINROOM 54
#define ESTRF_NOOWNERCOMMAND_TO_USER_INANOTHERROOM 55
#define ESTRF_OWNER_CHANGED 56
#define ESTRF_OWNER_CHANGED_AUTO 57
#define ESTRF_PLYR_IS_THERE 58
#define ESTRF_PLYR_NOT_LOGGED 59
#define ESTRF_PLYR_IS_THERE_LONELY 60
#define ESTRF_NOP_ON_SELF 61
#define ESTRF_JOIN_ANYROOM 62
#define ESTRF_NO_INVITATION_TO_USER_IN_SAME_ROOM 63
#define ESTRF_INVITATION_CALLED 64
#define ESTRF_INVITATION_CALLED_BY_OTHER 65
#define ESTRF_INVITER_LOGGEDOUT 66
#define ESTRF_INVITER_MOVED_ROOM 67
#define ESTRF_INVITATION_CHECK_ERROR 68
#define ESTRF_INVITITATION_ABORTED 69
#define ESTRF_INVITATION_DENIED 70
#define ESTRF_USER_BANISHIED 71
#define ESTRF_USER_KICKED 72
#define ESTRF_EX_ON 73
#define ESTRF_EX_OFF 74
#define ESTRF_YOUARE_SQUELDED_BY 75
#define ESTRF_USER_IS_NOT_RESPONDING 76
#define ESTRF_ALREADY_SQUELED 77
#define ESTRF_XSQUELED 78
#define ESTRF_YOUARE_XSQUELED_BY 79
#define ESTRF_SQUELED 80
#define ESTRF_YOUARE_SQUELED_BY 81
#define ESTRF_ALREADY_XSQUELED 82
#define ESTRF_SQUEL_COUNT_DROP 83

#define ESTRF_USER_START 100

#define ESTRF_PASSWORDCHANGED 110 // Password changed while logged in

void Randomize();

union UPacketValue
{
// CPU register specific
    WORD m_w;
    DWORD m_dw;
    float m_f;
    double m_dbl;
    SINT64 m_i64;
};
#define UPKV(pbPacket) ((UPacketValue*)(pbPacket))

class CDAPacket : public SOCBase_Class
{
// Variable parameters in the tail portion after processing the packet header...
public:
    BYTE m_bCmd;
    BYTE m_bReserved;
    WORD m_wLen;
    BYTE* m_pbPacket;
public:
    virtual CSOC* IsSOC() const { return NULL; }
    DWORD Push() const;
    void Pop(DWORD dw);
    BYTE* GetData(int nSize);
    BYTE* __cdecl Disassemble(const char* pcszFormat, ...);
    BYTE* __cdecl vDisassemble(const char* pcszFormat, va_list ap);
};

#define CSCODE_OK 0
#define CSCODE_ERR 0xff

#define CSCODE_CREATE 0
#define CSCODE_ADDED CSCODE_CREATE
#define CSCODE_INFO 2
#define CSCODE_CHANGE 3
#define CSCODE_CHANGED CSCODE_CHANGE
#define CSCODE_DELETE 4
#define CSCODE_DELETED CSCODE_DELETE

#define CSCODE_WHERE 5
#define CSCODE_SAY 6
#define CSCODE_INVITE 7
#define CSCODE_INVITE_RESULT 8
#define CSCODE_TO 9
#define CSCODE_SQUEL 10
#define CSCODE_XSQUEL 11
#define CSCODE_EX 12 // dummy user...
#define CSCODE_KICK 13 // C_PLYR_ACTN...
#define CSCODE_BANISH 14 // C_PLYR_ACTN...
#define CSCODE_CHOWNER 15 // C_PLYR_ACTN...

#define CSCODE_TITLE 20
#define CSCODE_PASSWORD 21
#define CSCODE_MAX 23

#define CSCODE_START 0xfa
#define CSCODE_MORE 0xfb
#define CSCODE_STOP 0xfc

#define S_PROG_INFO 200
#define C_PROG_INFO 200
// D - dwVersion

#define S_LOGN_INFO 210
#define C_LOGN_INFO 210
// s - ID, s - Password
#define S_LOGN_RSLT 211
// b - code(CSCODE_OK - ok or error code)

#define C_USER_INFO 215
// b - code(CSCODE_CREATE/CSCODE_INFO/CSCODE_CHANGE/CSCODE_DELETE), s - ID, s - Password...
#define S_USER_RSLT 215
// b - code(same w/ C_USER_INFO or CSCODE_ERR_...) if (code == CSCODE_INFO) additional user infos...

#define S_PLYR_INFO 220
// b - code(CSCODE_ADDED/CSCODE_CHANGED/CSCODE_DELETED/CSCODE_START/CSCODE_STOP), s - name, ...
#define C_PLYR_INFO 220
// b - code(CSCODE_NEXT/CSCODE_START/CSCODE_STOP)
#define C_PLYR_STAT 221
// s - name
#define S_PLYR_STAT 221
// b - code(CSCODE_OK - ok or error code)
#define C_PLYR_ACTN 222
// b - code(CSCODE_INFO, CSCODE_KICK, CSCODE_BANISH), s - name, ...
#define S_PLYR_ACTN 222
// b - code(CSCODE_KICK)

#define S_SRVR_INFO 225
// b - code(CSCODE_ADDED/CSCODE_CHANGED/CSCODE_DELETED/CSCODE_START/CSCODE_STOP), d - number, s - name, ...
#define C_SRVR_INFO 225
// b - code(CSCODE_NEXT/CSCODE_START/CSCODE_STOP)
#define C_SRVR_STAT 226
// d - name
#define S_SRVR_STAT 226
// b - code(CSCODE_OK - ok or error code)

#define S_ROOM_INFO 230
// b - code(CSCODE_ADDED/CSCODE_CHANGED/CSCODE_DELETED/CSCODE_START/CSCODE_STOP), d - number, s - name, b - Password exist, w - user count...
#define C_ROOM_INFO 230
// b - code(CSCODE_NEXT/CSCODE_START/CSCODE_STOP)
#define C_ROOM_STAT 231 // room number
#define S_ROOM_STAT 231
// b - code(CSCODE_OK - ok or error code)
#define C_ROOM_MAKE 232
// s - name, s - password
#define C_ROOM_JOIN 233
// d - number (leaving a Room = -1), s - password
#define S_ROOM_JOIN 233 // Result of C_ROOM_MAKE/C_ROOM_JOIN...
// b - code(CSCODE_OK or error code), d - number
#define C_ROOM_UPDS 234 // CSCODE_... + @

#define S_GAME_INFO 235
// b - code(CSCODE_ADDED/CSCODE_CHANGED/CSCODE_DELETED/CSCODE_START/CSCODE_STOP), d - number, s - name, ...
#define C_GAME_INFO 235
// b - code(CSCODE_NEXT/CSCODE_START/CSCODE_STOP)
#define C_GAME_STAT 236
// d - name
#define S_GAME_STAT 236
// b - code(CSCODE_OK - ok or error code)
#define C_GAME_RSLT 237

// same server/client command...
#define X_TEXT 250 // %s{Sender/Receiver ID}%s{notify string}%W{TX_...}
#define X_TEXTFMT 251 // %W{Format}%s{ParamFormat}[{params0}[{params2}[...]]]

#define C_FTP 253 //... see ftp.txt
#define S_FTP 254 //... see ftp.txt
#define X_KEEPALIVE 255 // no parameters, should be ignored...

#define TX_SYSTEM 0 // System message...
#define TX_NORMAL 1 // Regular chat text
#define TX_WARNING 2 // Warning
#define TX_ERROR 3 // Error
#define TX_FATAL 4 // Fatal error
#define TX_INFO 5 // Information result (e.g. user info)...
#define TX_SAY 6 // Whisper
#define TX_LOCAL 7 // local echo....
#define TX_UNKNOWN 0xffff // unknown...

typedef long (*PFN_TimerProc)(CTimerEvent& Timer);

class CSOCListen;
class CSOCClient;
typedef CSOCClient* (*PFN_CreateSOCClient)(const CSOCListen& SOCListen, DWORD dwCSCParam1, DWORD dwCSCParam2);

#ifdef WIN32
#pragma pack(push, 1)
#endif // WIN32
class CTimerEvent
{
    friend class CTimerPool;
public:
	PFN_TimerProc m_pfnTimerProc;
    DWORD m_dwTime;
	void* m_pVoid;
    // no function params...
    int m_nParam; // not initialized...
    DWORD m_dwParam; // not initialized...
protected:
    DWORD m_dwFrame;
};
#ifdef WIN32
#pragma pack(pop)
#endif // WIN32

#define CHK_TIMER_NOVOIDCHECK           0x8000
#define CHK_TIMER_ONCE                  0x0001

class CTimerPool : public TARRAY(CTimerEvent)
{
private:
    int m_nReEnter;
public:
    BOOL m_bDirty;
protected:
    DWORD m_dwPrecision;
    DWORD m_dwLastTicks;
private:
    DWORD m_dwFrame;
public:
    CTimerPool();
public:
    DWORD GetPrecision() const { return m_dwPrecision; }
    void SetPrecision(DWORD dwPrecision) { m_dwPrecision = dwPrecision; }
    DWORD GetLastTicks() const { return m_dwLastTicks; }
    void SetLastTicks(DWORD dwLastTicks) { m_dwLastTicks = dwLastTicks; }
public:
    void CheckTimers();
    CTimerEvent& AddTimer(PFN_TimerProc pfnTimerProc, DWORD dwTime, void* pVoid);
    BOOL DelTimer(int nIndex);
    BOOL DelTimers(PFN_TimerProc pfnTimerProc, void* pVoid, UINT uFlags, int nFromIndex = 0);
    BOOL DelTimersAll() { return DelTimers((PFN_TimerProc)NULL, NULL, 0, 0); }
    int FindTimer(PFN_TimerProc pfnTimerProc, void* pVoid, UINT uFlags, int nFromIndex = 0);
};

union UFmtParam
{
// CPU register specific
    BYTE m_b; // 'B'
    unsigned char m_c; // 'b'
    WORD m_w; // 'W'
    short m_s; // 'w'
    int m_n; // 'n'
    long m_l; // 'd'
    DWORD m_dw; // 'D'
    float m_f;
    double m_F;
    char* m_psz; // 's' or 'S' - don't mix...
    SINT64 m_i64; // '6' - 64 bit integer...
    // 'x' - binary data, wLen + Data
    // 'X' - mainly for Assemble, sub-packet copy... no info for disassemble
    // '!' - CPacketDumper*
};

class CPacketDumper
{
public:
public:
    CPacketDumper() {}
    virtual ~CPacketDumper() {}
    virtual BYTE* Disassemble(BYTE* pbPacket, BYTE* pbPacketEnd) = 0;
    virtual BYTE* Assemble(BYTE* pbPacket, BYTE* pbPacketEnd) = 0;
};

#ifdef WIN32
#pragma pack(push, 1)
#endif // WIN32
DECLARE_TARRAYREF(CPacket)
class
#ifdef WIN32
__declspec(novtable)
#endif // WIN32
CPacket
{
    friend class CSOCConnect;
protected:
    CSOC* m_pSOC;
private:
    // The block within must be contiguous...
    WORD m_wLen;
    BYTE m_ba[MAX_PACKET];
    //
public:
    CPacket(CSOC* pSOC);
    ~CPacket();
    void Reset();
    CSOC& GetSOC() const { ASSERT(this); return *m_pSOC; }
    const BYTE* GetPacketStart() const { return (const BYTE*)&m_wLen; }
    int GetLength() const { return m_wLen; }
    void SetLength(int nLen) { m_wLen = (WORD)(short)nLen; }
    BYTE* GetBuffer() const { return (BYTE*)&m_ba[0]; }
    int GetTotalLength() const { return sizeof(WORD) + m_wLen; }
    BYTE* GetLastPtr() const { return (BYTE*)&m_ba[m_wLen]; }
    BYTE* GetEndPtr() const { return (BYTE*)&m_ba[MAX_PACKET]; }
    void Prepare(int nSize) { m_wLen += (WORD)nSize; }
    BYTE* __cdecl Assemble(const char* pcszFormat, ...);
    BYTE* __cdecl vAssemble(const char* pcszFormat, va_list ap);
    BYTE* DisassembleParamsAndTextFmt(CSOCConnect& SOC, WORD wFmt, const char* pcszParamType, WORD& wTX);
protected:
    void DropSOC();
    BYTE* DoTextFmt(const char* pcszFormat, const UFmtParam aFPs[STRF_MAX_PARAMS], const char caParamType[/*STRF_MAX_PARAMS + 1*/]);
};
#ifdef WIN32
#pragma pack(pop)
#endif // WIN32

class CSockAddr : public sockaddr_in
{
public:
    CStr m_strTarget;
public:
    CSockAddr(int nPort, const char* pcszAddr = NULL, short sFamily = AF_INET);
    CSockAddr(int nPort, u_long ulAddr = INADDR_ANY, short sFamily = AF_INET);
public:
	sockaddr_in* GetSockAddrIn() { return this; }
    const char* GetTarget() const;
    u_long GetTargetAddr() const { return ntohl(sin_addr.s_addr); }
    void SetTarget(const char* pcszAddr);
    void SetTarget(u_long ulAddr);
    int GetPort() const;
    void SetPort(int nPort);
};
const char* GetHostAddress();

typedef void (CSOC::*PF_PacketProcessor)();
#define PFPP_CallBase ((PF_PacketProcessor)0) // do not change this line...
#define PFPP_Ignore CSOC::OnIgnore // why not? ((PF_PacketProcessor)1) 

#define MAX_PACKET_TYPE             256 // 1byte

/****************
start of variable PACKET_MAP definitions...
****************/

struct _SPacketMap
{
public:
	_SPacketMap* m_pBaseMap;
    PF_PacketProcessor* m_pPFPP;
public:
    PF_PacketProcessor TraceAndModifyBaseCall(BYTE bCmd, PF_PacketProcessor& refPFPP) const;
};

#define PACKET_MAP(CLASSNAME) _CAutoStart##CLASSNAME
#define PACKET_MAP_STATIC_INSTANCE(CLASSNAME) _g_AutoStart##CLASSNAME##Instance

#define DECLARE_PACKET_MAP(thisClass) friend class PACKET_MAP(thisClass); protected: static PF_PacketProcessor m_aPFPP[MAX_PACKET_TYPE]; static _SPacketMap m_PacketMap; protected: virtual _SPacketMap* GetPacketMap() const;

// start of BEGIN_PACKET_MAP_RAW, BEGIN_PACKET_MAP
#define BEGIN_PACKET_MAP_RAW(thisClass, basePacketMap) PF_PacketProcessor thisClass::m_aPFPP[MAX_PACKET_TYPE] = { PFPP_CallBase, }; _SPacketMap thisClass::m_PacketMap = { basePacketMap, &thisClass::m_aPFPP[0] }; _SPacketMap* thisClass::GetPacketMap() const { return &thisClass::m_PacketMap; } class PACKET_MAP(thisClass) { public: PACKET_MAP(thisClass)() {

#define BEGIN_PACKET_MAP(thisClass, baseClass) BEGIN_PACKET_MAP_RAW(thisClass, &baseClass::m_PacketMap)
// end of BEGIN_PACKET_MAP_RAW, BEGIN_PACKET_MAP

// start of PACKET_MAP_ENTRY_RAW, PACKET_MAP_ENTRY, PACKET_MAP_ENTRY_IGNORE
#define PACKET_MAP_ENTRY_RAW(thisClass, bCmd, PFPP) ASSERT((0 <= bCmd) && (bCmd < MAX_PACKET_TYPE)); thisClass::m_aPFPP[bCmd] = (PF_PacketProcessor)&PFPP;
#define PACKET_MAP_ENTRY(thisClass, bCmd, PFPP) PACKET_MAP_ENTRY_RAW(thisClass, bCmd, thisClass::PFPP)

#define PACKET_MAP_ENTRY_IGNORE(thisClass, bCmd) PACKET_MAP_ENTRY_RAW(thisClass, bCmd, PFPP_Ignore)
// end of PACKET_MAP_ENTRY & PACKET_MAP_ENTRY_IGNORE

// start of END_PACKET_MAP
#define END_PACKET_MAP(thisClass) } }; static PACKET_MAP(thisClass) PACKET_MAP_STATIC_INSTANCE(thisClass);
// end of END_PACKET_MAP

/****************
end of variable PACKET_MAP definitions...
****************/

class CFileTransfer : public TDBLNK(CFileTransfer*)
{
public:
    // Information received from server... - or information to relay to client...
    BOOL m_bClientSite;
    DWORD m_dwFTID;
    DWORD m_dwGUARD;
    CStr m_strFileNameRemote;
    DWORD m_dwTime;
    long m_lSize;

    UINT m_uMinBPS;
    UINT m_uMaxBPS;
    CStr m_strFileNameLocal;
    int m_nFile;
    long m_lPos;
    DWORD m_dwStartTime;
public:
    CFileTransfer(DWORD dwFTID, DWORD dwGUARD, const char* pcszFileNameRemote, const char* pcszFileNameLocal);
    virtual ~CFileTransfer();
    virtual BOOL Setup(DWORD& dwError);
    virtual BOOL Setup(DWORD dwTime, long lSize, DWORD& dwError);
    virtual int CheckReadPosition(long lPosition) const;
    int Close();
    void Start();
};

class CFTPArray : TARRAY(CFileTransfer*)
{
public:
public:
    CFTPArray();
    virtual ~CFTPArray();
    int Find(CFileTransfer* pFTP) const;
    CFileTransfer* CutIfExist(CFileTransfer* pFTP);
};

#ifdef USE_OVRIO
DECLARE_TARRAYREF(COVRIO);
#ifdef WIN32
#pragma pack(push, 1)
#endif // WIN32
class
#ifdef WIN32
__declspec(novtable)
#endif // WIN32
COVRIO : public TDoubleLinkRef<COVRIO*, WSAOVERLAPPED>
{
public:
    CSOC* m_pSOC;
    int m_nSize;
    BYTE m_ba[/*m_nSize*/];
public:
    COVRIO();
    ~COVRIO();
    DECLARE_STATIC_CONSTRUCTION(COVRIO);
};
#ifdef WIN32
#pragma pack(pop)
#endif // WIN32
COVRIO* newCOVRIO(CSOC* pSOC, const BYTE* pb, int nSize);
void deleteCOVRIO(COVRIO* pOVRIO);
void CALLBACK OVRIO_Completion(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
#endif // USE_OVRIO

#define SOCNEXT_EXCCLIENT           0x0001
#define SOCNEXT_EXCSERVER           0x0002
#define SOCNEXT_EXCLISTEN           0x0004

#define SOCNEXT_EXCINVALID          0x0010
#define SOCNEXT_EXCDROPPING         0x0020
#define SOCNEXT_EXCCONNECTTING      0x0080 // CSOCServer Only...

#define SOCNEXT_CHECKPROC           0x4000
#define SOCNEXT_REVERSE             0x8000

#define SOCNEXT_DEFAULT             (SOCNEXT_EXCINVALID|SOCNEXT_EXCDROPPING|SOCNEXT_EXCLISTEN|SOCNEXT_EXCCONNECTTING)

class CSOC : public CDAPacket
{
    friend class CSOCManager;
    DECLARE_PACKET_MAP(CSOC)
protected:
    int m_nLockCount;

    SOCKET m_hSocket;
    int m_nState;
	BOOL m_bSendLoop;
    CSOCManager* m_pSOCManager;
    CSockAddr m_Addr;
    // time - last Rcv/Snd
    DWORD m_dwKARcvTicks;
    DWORD m_dwKASndTicks;
    // time out to Drop...
    DWORD m_dwKARcvTimeOut;
#ifdef WIN32
    // Window handle typically used during Login process
    HWND m_hWndOwner;
#endif // WIN32
    // For auto NULL assignment of socket pointer typically used as a global variable
    PSOC* m_ppSOC;
    // ID used in user program to identify a socket
    // Mainly for servers: assigns a unique ID each time a client connects
    // Used to distinguish each connection when a client connects to multiple servers (Star topology)
    // When a client connects to only one server: used to check if client is connecting or already connected
    // Use CSOCManager::FindSOC(CSOC_SERVER_ID==default)->GetConnectionState().
    // Must include limits.h.
    // Default values at CSOC creation: CSOCServer==CSOC_SERVER_ID(INT_MIN), CSOCClient==CSOC_CLIENT_ID(0), CSOCListen==CSOC_LISTEN_ID(-1)
    union {
    int m_nID;
    UINT m_uID;
    void* m_pvID;
    } m_ID;
protected:
    DWORD m_dwDroppingTicks;
public:
    TDeQueueFixed<BYTE,RCV_MAX, CNullClass> m_aRcv;
#ifdef USE_OVRIO
    COVRIO* m_pOVRIOStart;
#else // !USE_OVRIO
    TDeQueueFixed<BYTE,SND_MAX, CNullClass> m_aSnd;
#endif // USE_OVRIO
protected:
    // don't use this - use get/set functions only...
    union {
    struct {
    DWORD m_xAllowAccept: 1; // CSOCListen Only...
    DWORD m_xUseSelect: 1; // Use select() for Acception Check...
    DWORD m_xAutoDelete: 1;
    DWORD m_xAdded: 1;
    DWORD m_xIsConnected: 1;
    DWORD m_xIsConnecting: 1;
    DWORD m_xDoDelSocket: 1;
    DWORD m_xIsDropping: 1;
    DWORD m_xIsSetupFailure: 1;
    DWORD m_xGracefulLocal: 1;
    DWORD m_xErrSEND: 1;
    DWORD m_xErrRECEIVE: 1;
    DWORD m_xGracefulRemote: 1;
    DWORD m_xAbortyLocal: 1;
    DWORD m_xAbortyRemote: 1;
    DWORD m_xLoginStarted: 1; // Must be set directly by the caller.
    DWORD m_xLoginOK: 1; // Must be set directly by the caller.
    } m_DW;
    DWORD m_dwVarFlags;
    };
public:
    CSOC();
    virtual ~CSOC();
    void Reset();
public:
    operator SOCKET () const { return m_hSocket; }
    SOCKET GetSocket() const { return m_hSocket; }
    BOOL IsValidSafe() const { return this && (m_hSocket != INVALID_SOCKET); }
    BOOL IsValid() const { return m_hSocket != INVALID_SOCKET; }
    int GetID() const { return m_ID.m_nID; }
    void SetID(int nID) { m_ID.m_nID = nID; }
    void Lock() { m_nLockCount++; }
    void Unlock() { ASSERT(m_nLockCount > 0); m_nLockCount--; }
    int GetLockCount() const { return m_nLockCount; }
    BOOL IsConnecting() const { return m_DW.m_xIsConnecting; }
    BOOL IsConnected() const { return m_DW.m_xIsConnected; }
    BOOL IsDroppingState() const { return m_DW.m_xIsDropping; }
	CSockAddr& GetSockAddr() const { return *(CSockAddr*)&m_Addr; }
    void SetRcvTimeOut(DWORD dwTimeOut) { m_dwKARcvTimeOut = dwTimeOut; }
#ifdef WIN32
    HWND GetOwnerHWND() const { return m_hWndOwner; }
    void SetOwnerHWND(HWND hWndOwner) { m_hWndOwner = hWndOwner; }
#endif // WIN32
    PSOC* GetSOCPP() const { return m_ppSOC; }
    void SetSOCPP(PSOC* ppSOC) { if (this) m_ppSOC = ppSOC; }
    BOOL GetLoginStarted() const { return m_DW.m_xLoginStarted; }
    void SetLoginStarted(BOOL bLoginStarted) { m_DW.m_xLoginStarted = !!bLoginStarted; }
    BOOL GetLoginOK() const { return m_DW.m_xLoginOK; }
    void SetLoginOK(BOOL bLoginOK) { m_DW.m_xLoginOK = !!bLoginOK; }
    BOOL GetUseSelect() const { return m_DW.m_xUseSelect; }
    void SetUseSelect(BOOL bUseSelect) { m_DW.m_xUseSelect = !!bUseSelect; }
	BOOL GetSendLoop() const { return m_bSendLoop; }
	void SetSendLoop(BOOL bSendLoop) { m_bSendLoop = bSendLoop; }
    const char* GetTarget() const { return m_Addr.m_strTarget; }
    void SetTarget(const char* pcszTarget) { m_Addr.SetTarget(pcszTarget); }
    void SetTarget(u_long ulAddr) { m_Addr.SetTarget(ulAddr); }
    int GetPort() const { return m_Addr.GetPort(); }
    void SetPort(int nPort) { m_Addr.SetPort(nPort); }
    BOOL GetAutoDelete() const { return m_DW.m_xAutoDelete; }
    void SetAutoDelete(BOOL bAutoDelete) { m_DW.m_xAutoDelete = !!bAutoDelete; }
    int GetState() const { return m_nState; }
    void SetState(int nState) { m_nState = nState; }
	CSOCManager& GetSOCManager() const { return *m_pSOCManager; }
    void SetSOCManager(CSOCManager* pSOCManager) { ASSERT(!m_pSOCManager || !pSOCManager); m_pSOCManager = pSOCManager; }
    BOOL IsAdded() const { return m_DW.m_xAdded; }
    void SetAdded(BOOL bAdded) { m_DW.m_xAdded = !!bAdded; }
    CFileTransfer* FindFTP(DWORD dwFTID) const;
    CFileTransfer* FindFTP(const char* pcszFileNameRemote) const;
public:
    int GetSignatureFromPort(int nPort) const;
    int GetConnectionState() const;
    void Drop();
public:
    BOOL DoSend(const BYTE* pbBuf, int nSize);
    BOOL DoSendCmdWOP(BYTE bCmd);
    void DoSendLoginOK();
    void DoSendLoginError();
    void DoSendLoginError(WORD wErrFMT);
    BOOL DoSend(const CPacket& Packet);
    BOOL DoSendX(BYTE bCmd, const void* pData, int nLen);
    BOOL __cdecl DoSendX(const char* pcszFormat, ...);
    BOOL DoSendText(BYTE bCmd, const char* pcsz);
    BOOL DoSendText(const char* pcszID, const char* pcsz, WORD wTX = TX_NORMAL);
    BOOL __cdecl DoSendFmt(WORD wFmt, const char* pcszParamType = NULL, ...);
    BOOL vDoSendFmt(WORD wFmt, const char* pcszParamType, va_list ap);
public:
    virtual CSOC* IsSOC() const { return (CSOC*)this; }
    virtual BOOL CheckProc(DWORD dwParam) { return TRUE; }
    virtual DWORD GetSendTimeOut() const { return 5 * 1000; }
    virtual BOOL IsListenSocket() const { return FALSE; }
    virtual BOOL IsClientSocket() const { return FALSE; }
    virtual BOOL IsServerSocket() const { return FALSE; }
    virtual UINT GetType() const { return 0; }
    virtual UINT GetType3() const = 0;
    virtual const CTEXTFmtArray& GetTEXTFmtArray();
    virtual const CTEXTFmt& GetTEXTFmt(WORD wFmt);
	virtual BOOL DoProcessPacket();
    virtual void OnOpen();
    virtual BOOL OnSetupSocket();
    virtual void OnAddSocket();
    virtual void OnDropping();
    virtual void OnDelSocket();
    virtual void OnShutdown();
    virtual void OnClose();
    virtual void OnDelete();
    virtual void OnSendKeepAlive();
#if !defined(USE_OVRIO)
	virtual BOOL OnOverflow(int nSizeReq, int nRetry, DWORD dwTimeBegin);
#endif // !defined(USE_OVRIO)
public: // packet handler map
    void OnIgnore();
    void OnKeepAlive();
#ifdef USE_OVRIO
    COVRIO* newCOVRIO(const BYTE* pb, int nSize);
    void deleteCOVRIO(COVRIO* pOVRIO);
    void OnOVRIO_Completion(DWORD dwError, DWORD cbTransferred, COVRIO* pOVRIO, DWORD dwFlags);
#endif // USE_OVRIO
    int aSnd_GetDataSize();
protected:
    BOOL IsSendPossible() const { return !(m_DW.m_xErrSEND || m_DW.m_xAbortyRemote || m_DW.m_xGracefulLocal); }
    BOOL IsReceivePossible() const { return !(m_DW.m_xErrRECEIVE || m_DW.m_xAbortyRemote || m_DW.m_xGracefulRemote); }
    BOOL DoClose();
    void CheckAutoDelete();
    void SetDroppingTicks();
    BOOL CreateAndAttach();
    void CallOnOpen(SOCKET soc);
    void CallOnDropping();
    void CallOnShutdown(int nHow);
    void CallOnClose();
    void CallOnDelete();
protected:
    void DoPacketProcessing();
    BOOL OnReceive();
    int DoReceive(BYTE* pbBuf, int nLen);
    int OnSend();
protected:
    void SendKeepAlive();
    void ResetKeepAliveAll();
    void ResetKeepAliveRcv();
    void ResetKeepAliveSnd();
protected:
    EException GetExceptionCode(int nErrorCode);
    EException DoErrorReport(int nErrorCode, BOOL bDropIfErrorOccur = FALSE);
    EException DoExceptions(EException eException, BOOL bDropIfErrorOccur = FALSE);
};

class CSOCListen : public CSOC
{
    friend class CSOC;
    friend class CSOCManager;
protected:
    PFN_CreateSOCClient m_pfnCSC;
    DWORD m_dwCSCParam1;
    DWORD m_dwCSCParam2;
    DWORD m_dwListenTime;
	DWORD m_dwLastCheck;
public:
    CSOCListen(int nPort = 0, DWORD dwListenTime = 250);
    virtual ~CSOCListen();
	DWORD GetListenTime() const { return m_dwListenTime; }
    void SetListenTime(DWORD dwListenTime) { m_dwListenTime = dwListenTime; }
    BOOL GetAllowAccept() const { return m_DW.m_xAllowAccept; }
    void SetAllowAccept(BOOL bAllowAccept) { m_DW.m_xAllowAccept = !!bAllowAccept; }
    void GetCSCParam(PFN_CreateSOCClient& pfnCSC, DWORD& dwCSCParam1, DWORD& dwCSCParam2) const;
    void SetCSCParam(PFN_CreateSOCClient pfnCSC, DWORD dwCSCParam1, DWORD dwCSCParam2);
public:
    virtual BOOL IsListenSocket() const { return TRUE; }
    virtual UINT GetType3() const { return SOCNEXT_EXCLISTEN; }
    virtual BOOL BindAndListenSocket();
    virtual void OnAcception();
    virtual CSOCClient* OnNewClientSocket() const;
    virtual BOOL OnAddrAcceptionCheck(const sockaddr_in& Addr) { return TRUE; }
    virtual BOOL DoProcessPacket() { m_aRcv.FreeAll(); return FALSE; }
};

class CSOCConnect : public CSOC
{
    friend class CSOC;
    friend class CSOCManager;
    DECLARE_PACKET_MAP(CSOCConnect)
protected:
    time_t m_tConnection;
    DWORD m_dwLingerTime;
    CFileTransfer* m_pFTPStart;
public:
    CSOCConnect(DWORD dwLingerTime);
    virtual ~CSOCConnect();
public:
    time_t GetConnectionTime() const { return m_tConnection; }
    DWORD GetConnectionTicks() const;
    DWORD GetConnectionTimeString(CStr& strTime, BOOL bForceShowMin = FALSE, BOOL bForceShowHour = FALSE) const;
    void SetConnectionTime();
    DWORD GetLingerTime() const { return m_dwLingerTime; }
    void SetLingerTime(DWORD dwLingerTime) { m_dwLingerTime = dwLingerTime; }
public:
    BOOL DoFileTransfer(DWORD dwGUARD, const char* pcszFileNameRemote);
    CFileTransfer* FindFTP(DWORD dwFTID) const;
    CFileTransfer* FindFTP(const char* pcszFileNameRemote) const;
    void DoFTPCloseAllAsError();
public:
    virtual void DoTextDisplay(const char* pcszID, const char* pcszMsg, WORD wTX);
public: // FTP virtual...
    virtual void OnFTPFileInfoNotify(CFileTransfer* pFTP);
    virtual void OnFTPError(CFileTransfer* pFTP, long lErrorCode);
    virtual const char* OnFTPDirectory(BOOL bClientSite) const;
    virtual const char* OnFTPFileNameValidation(const char* pcszFileName) const;
    virtual void OnFTPFileMap(DWORD dwGUARD, const char* pcszFileNameRemote, CStr& strFileNameLocal, BOOL bClientSite) const;
    virtual void OnFTPSrvrError(DWORD dwGUARD, const char* pcszFileNameRemote, DWORD dwError);
    virtual void OnFTPNotFound(DWORD dwFTID, BOOL bClientSite);
    virtual CFileTransfer* OnAllocFileTransfer(DWORD dwGUARD, const char* pcszFileNameRemote, DWORD& dwError);
    virtual CFileTransfer* OnAllocFileTransfer(DWORD dwFTID, DWORD dwGUARD, const char* pcszFileNameRemote, DWORD dwTime, long lSize, DWORD& dwError);
    virtual CFileTransfer* OnNewFileTransfer(DWORD dwFTID, DWORD dwGUARD, const char* pcszFileNameRemote, const char* pcszFileNameLocal);
    virtual void OnFreeFileTransfer(CFileTransfer* pFTP);
    virtual void OnFTPRead(CFileTransfer* pFTP, long lSize);
    virtual void OnFTPWrite(CFileTransfer* pFTP, long lPosition);
    virtual void OnFTPTerminate(CFileTransfer* pFTP);
    virtual BOOL DoFTPWrite(CFileTransfer* pFTP, const BYTE* pb, long lSize);
    virtual long DoFTPRead(CFileTransfer* pFTP, const BYTE* pb, long lSize);
public: // packet handler map
	void OnText();
    void OnTextFmt();
    void OnFTP();
protected:
    BOOL CheckDroppingTicks(DWORD dwTicks);
};

class CSOCClient : public CSOCConnect
{
    friend class CSOC;
    friend class CSOCListen;
    friend class CSOCConnect;
    friend class CSOCServer;
    friend class CSOCManager;
    DECLARE_PACKET_MAP(CSOCClient)
public:
    TARRAYREF(CStr) m_astrGroups;
public:
    CSOCClient(DWORD dwLingerTime = MY_LINGER_TIME);
    virtual ~CSOCClient();
public:
    virtual BOOL IsClientSocket() const { return TRUE; }
    virtual UINT GetType3() const { return SOCNEXT_EXCCLIENT; }
    virtual BOOL OnCheckIDAndPassword(const char* pcszID, const char* pcszPW);
    virtual void OnAccept();
public: // packet handler map
protected:
    void CallOnAccept();
};

class CSOCServer : public CSOCConnect
{
    friend class CSOC;
    friend class CSOCListen;
    friend class CSOCConnect;
    friend class CSOCClient;
    friend class CSOCManager;
    DECLARE_PACKET_MAP(CSOCServer)
public:
    CSOCServer(const char* pcszTarget = NULL, int nPort = 0, DWORD dwLingerTime = MY_LINGER_TIME);
    virtual ~CSOCServer();
public:
    void CallOnConnectionTimeOut(); // do not use the function, should be protected but...
public:
    virtual BOOL IsServerSocket() const { return TRUE; }
    virtual UINT GetType3() const { return SOCNEXT_EXCSERVER; }
    virtual void OnConnect();
    virtual void OnConnectionTimeOut();
public: // packet handler map
protected:
    BOOL DoConnect();
    void CallOnConnect();
};

class CSOCUDP
{
protected:
    CSockAddr m_Addr;
    SOCKET m_hSocket;
    int m_nPort;
public:
    CSOCUDP(int nPort = 0);
    virtual ~CSOCUDP();
    CSockAddr& GetSockAddr() const { return *(CSockAddr*)&m_Addr; }
    operator SOCKET () const { return m_hSocket; }
    SOCKET GetSocket() const { return m_hSocket; }
    BOOL IsValidSafe() const { return this && (m_hSocket != INVALID_SOCKET); }
    BOOL IsValid() const { return m_hSocket != INVALID_SOCKET; }
    int GetPort() const { return m_nPort; }
    UINT GetMAX_MSG_SIZE() const;
    BOOL CreateAndBind(int nPort = 0);
    void Drop();
    BOOL IsReadable() const;
    BOOL IsWritable() const;
    int Send(const void* pBuffer, int nLen, DWORD dwAddr, int nPort);
    int Recv(void* pBuffer, int nLen, DWORD& dwAddr, int& nPort);
    int Send(const void* pBuffer, int nLen, const sockaddr_in& Addr);
    int Recv(void* pBuffer, int nLen, sockaddr_in& Addr);
};

template <class CSOCClass>
class TSOCArray : public TARRAY(CSOCClass*)
{
public:
public:
    int FindIndex(CSOCClass* pSOC) const
    {
        int nSize = GetSize();
        CSOCClass** pp = &GetAt(nSize);

        while(nSize-- > 0) {
            if (*--pp == pSOC)
                break;
        }

        return nSize;
    }
    int DelIfExist(CSOCClass* pSOC)
    {
        int nIndex = FindIndex(pSOC);

        if (nIndex != -1) {
            Del(nIndex);
        }

        return nIndex;
    }
};

#ifdef WIN32
#define XFD_ARRAY fd_set
#else // !WIN32
struct XFD_ARRAY
{
public:
    int fd_count;
    SOCKET fd_array[FD_SETSIZE];
};
#endif // WIN32

class XFD_SET : protected XFD_ARRAY
{
private:
#if !defined(WIN32)
    fd_set m_FD_SET;
#endif // !defined(WIN32)
public:
    XFD_SET();
    XFD_SET(SOCKET soc);
    XFD_SET(const XFD_SET& that);
    int GetCount() const { return fd_count; }
    SOCKET* GetFDs() const { return (SOCKET*)&fd_array[0]; }
    void Assign(const XFD_SET& that);
    int FindIndexFromHandle(SOCKET soc) const;
    void ZERO();
    BOOL ISSET(SOCKET soc);
    void SET(SOCKET soc);
    void CLR(SOCKET soc);
    void CLR_Index(int nIndex);
    XFD_ARRAY& GetFD_ARRAY() const;
    fd_set& GetFD_SET() const;
    int Select(int nType);
#ifdef _DEBUG
    void CheckHandleMap() const;
#endif // _DEBUG
};

class CFDSet : protected XFD_SET
{
protected:
    PSOC m_paSOCs[FD_SETSIZE];
public:
    CFDSet();
    virtual ~CFDSet();
public:
    CSOC* GetSOCFromHandle(SOCKET soc) const;
    int GetCount() const { return fd_count; }
	PSOC* GetStartOfSOCs() const { return (PSOC*)&m_paSOCs[0]; }
    PSOC FindSOC(SOCKET soc, int& nIndex) const;
#ifdef _DEBUG
    void CheckHandleMap() const;
#endif // _DEBUG
    void SET(const CSOC& SOC);
    void CLR(const CSOC& SOC);
    void CLR_Index(int nIndex);
protected:
    BOOL AddSOC(CSOC& SOC);
    void DelSOC(CSOC& SOC);
    void DelSOCFromIndex(CSOC& SOC, int nIndex);
};

class CDroppings : protected TSOCArray<CSOC>
{
    friend class CSOCManager;
public:
public:
    void AddWithFlush(CSOC* pSOC);
};

template <int nFDArrayMAX>
class TFD_SET_Dynamic
{
private:
    int m_nLevel;
    XFD_SET* m_pFD_SET;
    XFD_SET m_aFD_SET[nFDArrayMAX];
    TARRAYPTR2(XFD_SET*) m_paFD_SET;
public:
    TFD_SET_Dynamic()
    {
        m_nLevel = 0;
        m_pFD_SET = NULL;
        m_paFD_SET.SetGrowBy(3);
    }
    ~TFD_SET_Dynamic()
    {
        ASSERT(m_nLevel == 0);
    }
    void Alloc()
    {
        if (m_nLevel < nFDArrayMAX) {
            m_pFD_SET = &m_aFD_SET[m_nLevel];
        } else {
            XFD_SET* p = (XFD_SET*)malloc(sizeof(XFD_SET));
            m_paFD_SET.Add() = p;
            m_pFD_SET = p;
        }
        m_nLevel++;
    }
    void Free()
    {
        ASSERT(m_nLevel > 0);
#ifdef _DEBUG
        if (m_nLevel <= nFDArrayMAX) {
            ASSERT(m_paFD_SET.GetSize() == 0);
        } else {
            ASSERT(nFDArrayMAX + m_paFD_SET.GetSize() == m_nLevel);
        }
#endif // _DEBUG

        if (--m_nLevel > 0) {
            if (m_nLevel < nFDArrayMAX) {
                m_pFD_SET = &m_aFD_SET[m_nLevel];
            } else {
                int nIndex = m_nLevel - nFDArrayMAX;
                m_paFD_SET.Del(nIndex);
                if (--nIndex < 0) {
                    m_pFD_SET = &m_aFD_SET[nFDArrayMAX - 1];
                } else {
                    m_pFD_SET = m_paFD_SET.At(nIndex);
                }
            }
        } else {
            ASSERT(m_nLevel == 0);
            m_pFD_SET = NULL;
        }
    }
    XFD_SET* GetFD_SET(const TSOCArray<CSOC>& aSOCs) const
    {
        ASSERT(m_pFD_SET && &aSOCs);

        int nSize = aSOCs.GetSize();
        XFD_SET& fd = *m_pFD_SET;
        XFD_ARRAY& fda = fd.GetFD_ARRAY();
        fda.fd_count = nSize;
#if !defined(WIN32)
        fd_set& u = fd.GetFD_SET();
        FD_ZERO(&u);
#endif // !defined(WIN32)
        SOCKET* pSOCKET = &fda.fd_array[0];
        CSOC** ppSOC = &aSOCs.GetAt(0);
        while(nSize-- > 0) {
            CSOC* pSOC = *ppSOC++;
            *pSOCKET++ = *pSOC;
#if !defined(WIN32)
            SOCKET soc = *pSOC;
            FD_SET(soc, &u);
#endif // !defined(WIN32)
        }

        return &fd;
    }
    XFD_SET* GetFD_SET(const XFD_SET& fd) const
    {
        ASSERT(m_pFD_SET && &fd);

        m_pFD_SET->Assign(fd);

        return m_pFD_SET;
    }
};

class CSOCManager : public CFDSet
{
    friend class CSOC;
    friend class CSOCListen;
    friend class CSOCConnect;
    friend class CSOCClient;
    friend class CSOCServer;
private:
    TFD_SET_Dynamic<STATIC_FDARRAY_MAX> m_FD_Dynamic;
protected:
    int m_nStarted;
// socsystem
    WORD m_wVersionRequested;
#ifdef WIN32
    WSADATA m_wsaData;
#endif // WIN32
// socmanager
    int m_nDroppingSockets;
    TSOCArray<CSOC> m_paConnectings;
    CDroppings m_paDroppings;
public:
    DWORD m_dwConnectTimeOut;
    CTimerPool m_aTimerPool;
public:
    CSOCManager(DWORD dwConnectTimeOut = 20000);
    virtual ~CSOCManager();
public:
	BOOL IsStarted() const { return m_nStarted > 0; }
	DWORD GetConnectTimeOut() const { return m_dwConnectTimeOut; }
    void SetConnectTimeOut(DWORD dwConnectTimeOut) { m_dwConnectTimeOut = dwConnectTimeOut; }
public:
    CSOCListen* GetListenSocket() const;
    CSOC* FindSOC(int nID = CSOC_SERVER_ID) const;
    CSOCServer* FindSOCServeLogin(int nID = CSOC_SERVER_ID) const;
    CSOC* GetNextSocket(UINT uType, UINT uMask, CSOC* pSOC, int& nIndex, DWORD dwParam = 0);
    BOOL DoListen(CSOCListen* pSOCListen, int nPort = 0);
    CSOCListen* DoListen(int nPort, PFN_CreateSOCClient pfnCSC, DWORD dwCSCParam1, DWORD dwCSCParam2);
    BOOL DoConnect(CSOCServer* pSOCServer, const char* pcszTarget = NULL, int nPort = 0);
    void DropAll(BOOL bWait = TRUE);
    EException OnExceptions(CSOC* pSOC, EException eException, BOOL bDropIfErrorOccur = FALSE);
public:
    virtual BOOL Init();
    virtual void Exit();
    virtual BOOL Startup();
    virtual void Shutdown();
    virtual BOOL SetupInitialOption();
    virtual BOOL InitSignal();
    virtual void ExitSignal();
    virtual void OnAddSocket(CSOC& SOC);
    virtual void OnDelSocket(CSOC& SOC);
    virtual void SingleStep();
    virtual CSOCClient* OnNewClientSocket(const CSOCListen& SOCListen, DWORD dwCSCParam1, DWORD dwCSCParam2) const;
protected:
    BOOL AddSocket(CSOC& SOC);
    void CallOnAddSocket(CSOC& SOC);
    void CallOnDelSocket(CSOC& SOC);
    void OnDrop(CSOC* pSOC);
    void DoConnecting(CSOCServer* pSOCServer, BOOL bConnecting);
    BOOL IsSafeToShutdown() const;
protected: // in single step
    void CheckDropping();
    void CheckFlush();
    void CheckReading();
protected:
    EException DoErrorReport(CSOC* pSOC, int nErrorCode, BOOL bDropIfErrorOccur = FALSE);
};

#endif // !defined(__MUGSOCS__)
