
/*---------------------------------------------------------------------------*/
/* tcpsocket.h                                                               */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

// Socket-Status defines
#define SST_CLOSED					0
#define	SST_OPENED					1
#define SST_CONNECTION_INITIATED	2
#define SST_CONNECTED				3
#define SST_DO_SHUTDOWN				4

#define SST_BIND_START              5
#define SST_BIND_DONE               6
#define SST_LISTEN                  7
#define SST_NEED_ACCEPT             8

class TCPSocketContext : public ISocketContext {
public:
};

class TCPSocketProvider : public ISocketProvider {
    friend class TCPSocket;
    virtual class TCPSocket * NewSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log);
public:
    virtual ISocketContext * CreateSocketContext(class IInstanceLog * const log);
    virtual ISocket * CreateSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log, bool useIPv6, class ISocketContext * socketContext = NULL /* currently ignored */);
};

class TCPSocket : public ISocket, public UIoContext, public UIoExec {
protected:
    IIoMux * const iomux;
    IInstanceLog * const log;
    TCPSocketProvider * const provider;
    USocket * user;
    bool useIPv6;
    int socketStatus;

    std::queue<struct SendBuffer *> sendQueue;

    struct sockaddr_storage connectedAddr;
    struct sockaddr_storage localAddr;

    void * recvBuf;
    size_t recvBufLen;
    size_t recvBufAlreadyRead;
    bool recvBufReadPartial;
    int execCnt;
    bool shutDown;
    shutdownreason_t shutdownReason;
    bool isServer;
    bool createdByServer;
    int remoteFD;
    char * remoteAddress;
    char * serverName;

    bool ConnectIPv4(const char * address);
    bool ConnectIPv6(const char * address);
    bool BindIPv4(const char * address, word port);
    bool BindIPv6(const char * address, word port);

    void AddNewSendBuffer(const void * buf, size_t len);
    bool SendQueuedData();
    bool ReceiveData();
    bool DoAccept();
    void DoShutdown();
    void SetTCPKeepAlive();

    virtual void FDNotify(bool notifyRX, bool notifyTX) {};

    inline void UserSocketConnectComplete(const char * localAddr, word localPort);
    inline void UserSocketBindResult(const char * localAddr, word localPort);
    inline void UserSocketListenResult(const char * remoteAddr, word remotePort);
    inline void UserSocketAcceptComplete(USocket * const remoteUser, TCPSocket * remoteSocket, const char * localAddr, word localPort);
    inline void UserSocketShutdown(shutdownreason_t reason);
    inline void UserSocketSendResult();
    inline void UserSocketRecvResult(void * buf, size_t len);
    inline void UserSocketRecvCanceled(void * buf);

public:
    TCPSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log, TCPSocketProvider * provider);
    virtual ~TCPSocket();
    virtual void OpenSocket(bool useIPv6);

    virtual void Connect(const char *address, const char * serverName = NULL) override;
    void GetLocalAddress(char * localAddr, unsigned size, word * localPort) override;
    void GetRemoteAddress(char * remoteAddr, unsigned size, word * remotePort) override;
    void Bind(const char * localAddr, word localPort, bool reuseAddr) override;
    void Bind(const char * localAddr = NULL, word localPort = 0) override { Bind(localAddr, localPort, true); };
    void Listen() override;
    void Accept(USocket * const remoteUser) override;
    void Shutdown() override;
    void Send(const void * buf, size_t len) override;
    void Recv(void * buf, size_t len, bool recvPartial = false) override;
    void SetQoS(qostraffictype_t type) override;
    void SetUser(class USocket * const user, bool * createdByServer, const char ** remoteAddress, const char ** serverName) override;

    void IoContextNotify(bool indRX, bool indTX) override;
    void IoExec(void * context) override;
};
