/*---------------------------------------------------------------------------*/
/* turn.h                                                                    */
/* copyright (c) innovaphone 2017                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

typedef struct turn_peer_s {
    IPaddr addr;
    word port;
    byte state;
} turn_peer_t;

class TurnUser {
public:
    word turnNum;
    char *turnUsr;
    char *turnPwd;

    virtual void TurnSend(void * ctx, char * data, int len) = 0;
    virtual void TurnTimerStart(void * ctx, int to) = 0;
    virtual void TurnTimerStop(void * ctx) = 0;
    virtual void TurnAllocated(void * ctx) = 0;
    virtual void TurnReleased(void * ctx) = 0;
    virtual void TurnFailed(void * ctx, dword code, const char * errorText) = 0;
};

class Turn {
    class IInstanceLog * const log;
    class IIoMux * iomux;
    class TurnUser *user;
    void * userCtx;

    OS_GUID id;

    bool rtcp;
    word startChannel;
    unsigned int state;
    dword allocateFlags;
    bool turnRtcp;
    dword retries;
    char * sendData;
    int sendDataLen;
    word boundChannel;
    unsigned int peersAllocated;
    char *realm;
    char *nonce;
    IPaddr sendAddr;
    word sendPort;
    IPaddr alternateAddr;
    word alternatePort;
    union {
        byte b[4];
        word w[2];
        dword d[1];
    } dataHeader;
    bool turnTcp;

    word channelBind(IPaddr & addr, word port);

    enum error_codes {
        Error_connection_failed,
        Error_allocation_failed,
        Error_abnormal_release_after_allocation,
        Error_abnormal_release_after_refresh,
        Error_abnormal_shutdown,
    };

public:
    byte reservationToken[8];
    IPaddr localAddr;
    word localPort;
    IPaddr turnAddr;
    word turnPort;
    IPaddr turnAltAddr;
    word turnAltPort;
    IPaddr relayedAddr;
    word relayedPort;
    IPaddr mappedAddr;
    word mappedPort;
    turn_peer_t *peerTable;
    unsigned int peerCount;

public:
    Turn(class IIoMux * iomux, class IInstanceLog * const log, class TurnUser *user, void * userCtx, bool rtcp);
    virtual ~Turn();

    void timeout();
    bool bind(IPaddr & addr, word port);
    void stunSend(char * data, int len, IPaddr addr, word port);
    void startAllocation(dword flags);
    bool isTurnPacket(char * data, int len);
    bool iceTurnRecv(class ISocket * socket, IPaddr srcAddr, word srcPort, IPaddr dstAddr, word dstPort, char * data, int len);
    bool isAllocating();
    void clearAllBinds();
    void release();
    void releaseAllocation();
    bool isIdle() { return (state == 0); };
    size_t turnTcpParse(const byte * tcpData, size_t len);
    void turnTcpConnecting();
    void turnTcpShutdown();
};
