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

#define SCTP_PROC_ELAPSED_TICKS    4   // 100 mseconds

class RxSctpPackets {
public:
    RxSctpPackets(byte *data, int len, unsigned int tsn, bool start, bool end, dword prot, int timer, word streamId) {
        this->data = data;
        this->len = len;
        this->tsn = tsn;
        this->acked = false;
        this->dup = 0;
        this->start = start;
        this->end = end;
        this->prot = prot;
        this->timer = timer;
        this->streamId = streamId;
        this->next = this->prev = NULL;
    };

    byte *data;
    int len;
    unsigned int tsn;
    bool acked;
    int dup;
    bool start;
    bool end;
    dword prot;
    int timer;
    word streamId;
    class RxSctpPackets *next;
    class RxSctpPackets *prev;
};

class SctpMsg : public istd::listElement<SctpMsg> {
public:
    SctpMsg(char * msg, int len) {
        this->msg = (char *)malloc(len);
        memcpy(this->msg, msg, len);
        this->len = len;
    };
    ~SctpMsg() { if(this->msg) free(this->msg); };

    char * msg;
    int len;
};

class SctpTxTsn : public istd::listElement<SctpTxTsn> {
public: 
    SctpTxTsn(unsigned int tsn) {
        this->tsn = tsn;
    }
    unsigned int tsn;
};

class SctpNumSeqTsn : public istd::listElement<SctpNumSeqTsn> {
public:
    SctpNumSeqTsn(unsigned num, word seq, unsigned int start, unsigned int end, int dataLength) {
        this->num = num;
        this->seq = seq;
        this->start = start;
        this->end = end;
        this->count = end-start+1;
        this->lost = false;
        this->dataLength = dataLength;
    };
    ~SctpNumSeqTsn() {
        if(!this->rxTsns.empty()) {
            while(this->rxTsns.front()) {
                delete this->rxTsns.front();
            }
        }
    };

    unsigned num;
    word seq;
    unsigned int start;
    unsigned int end;
    int count;
    bool lost;
    int dataLength;
    istd::list<SctpTxTsn> rxTsns;
};

class SctpRetransmissions: public istd::listElement<SctpRetransmissions> {
public:
    SctpRetransmissions(unsigned int tsn, int timerTicks) {
        this->tsn = tsn;
        this->timerTicks = timerTicks;
    };

    unsigned int tsn;
    int timerTicks;
};

class USctpRtc {
public:
    virtual void SctpRtcConnected(bool err) = 0;
};

class SctpRtc {
    class IInstanceLog * const log;
    class USctpRtc * user;

    word srcSctpNumber;
    word dstSctpNumber;
    dword initDstTag;
    bool fwdTsnSupported;
    dword initSrcTag;
    int winSrcCredit;
    dword inSrcStreams;
    dword outSrcStreams;
    dword inDstStreams;
    dword outDstStreams;
    dword initTsnSrc;
    bool handlingDone;
    word streamId;
    word seqNumber;
    word lastSeqSack;
    int dataToAcked;
    dword cumDstTsnAck;
    int winDstCredit;
    int initWinDstCredit;
    dword lastProcTsnRx;
    dword initTsnDst;
    int elapsedTimer;
    dword lastTsnSrcAcked;
    int heartbeatCnt;
    int heartbeatCntSent;
    bool isPeerAlive;
    int localWinDstCredit;
    int tempWinDstCredit;
    bool doZeroProving;
    int timerWzp;
    int initTimerWzp;
    bool initAckMessage;
    bool cookieEchoAckMessage;
    int numHandshakeTx;
    unsigned int lastTsnRx;
    bool breakHandshake;
    unsigned int timerLastTsnAcked;
    bool sctpServer;
    unsigned int foundGapTsn;
    unsigned int forwardGapTsn;

    int totalRxPackets;
    int dropRxPackets;
    int totalTxPackets;
    int dropTxPackets;
    int dropMediaTx;
    int numAllowPacketsTx;
    int dropSeveralTx;
    int dropMediaRx;
    int numAllowPacketsRx;
    int dropSeveralRx;

    class RxSctpPackets *pDataQueue;
    istd::list<class SctpMsg> pTxSctpQueue;
    istd::list<class SctpRetransmissions> pRetrQueue;
    istd::list<class SctpNumSeqTsn> sctpNumSeqTsns;
    
    byte * rxStateCookie;
    int rxCookieLen;
    byte* txStateCookie;
    int txCookieLen;
    byte *pInitMsg;
    int initMessageLen;

    word sequenceTable[4096];

    static const unsigned int DataMsg = 0;
    static const unsigned int InitMsg = 1;
    static const unsigned int InitAckMsg = 2;
    static const unsigned int DataAckMsg = 3;
    static const unsigned int HeartbeatMsg = 4;
    static const unsigned int HeartbeatAckMsg = 5;
    static const unsigned int AbortMsg = 6;
    static const unsigned int ShutdownMsg = 7;
    static const unsigned int ShutdownAckMsg = 8;
    static const unsigned int ErrorMsg = 9;
    static const unsigned int EchoMsg = 10;
    static const unsigned int EchoAckMsg = 11;
    static const unsigned int ShutdownOkMsg = 14;
    static const unsigned int ForwardCumTsnMsg = 192;

    int ProcessInit(byte *pData, dword len);
    int ProcessInitAck(byte * pData, int len);
    int ProcessCookieEcho(byte *pData, dword len);
    int ProcessHeartbeat(byte *pData, dword len);
    int ProcessAckHeartbeat(byte *pData, dword len);
    int ProcessAbort(byte *pData, dword len);
    int ProcessData(byte *pData, dword len);
    void ProcessDataQueue(void);
    void ProcessWebrtcDcepMsg(byte *pData, dword len, word streamId);
    void ProcessWebrtcDcepOkMsg(byte *pData, dword len);
    int ProcessSack(byte *pData, dword len);
    int ProcessForwardTsn(byte *pData, int len);
    int ProcessError(byte *pData, int len);

    bool GenerateCookieEchoMessage(void);
    bool GenerateCookieAckMessage(void);
    bool GenerateShutdownAckMessage(void);
    bool GenerateShutdownCompleteMessage(void);
    bool GenerateHeartbeatAckMessage(byte *heartbeat, int hblen);
    bool GenerateHeartbeatMessage(void);
    bool GenerateInitMessage(void);
    bool GenerateInitAckMessage(void);
    void GenerateSackMessage(unsigned int rTsn);
    void GenerateSackMessageWithGaps(void);
    void GenerateWebrtcDcepMessage(void);
    void GenerateWebrtcDcepOkMessage(void);
    void GenerateForwardTsn(unsigned short lastSeq, unsigned int startTsn, unsigned int lastTsnAcked);

    bool validateCrc32(unsigned char *buffer, dword length);
    dword generateCrc32c(unsigned char *buffer, dword length);
    void randBytes(unsigned char *dst, dword length);
    void resendPacket(unsigned int sTsn, unsigned int eTsn);
    void clearSctpData(void);
    bool findRetrasmittedTsn(unsigned int sTsn);
    bool sendSctpData(char * buf, int len, unsigned num);
    bool windowZeroProbe(word streamSeq, unsigned int lastAck);
    void AckTsnSequence(unsigned int tsn);
    void MarkLostTsnSequence(unsigned int tsn);

public:
    SctpRtc(class USctpRtc * user, class IInstanceLog * const log, int dropMediaTx, int dropMediaRx);
    ~SctpRtc();

    void DoSctpHandshake(bool server, word dstSctpNumber = DEFAULT_SCTP_CHANNEL_NUMBER, word srcSctpNumber = DEFAULT_SCTP_CHANNEL_NUMBER);

    void SctpTimeout(int timer);
    void SctpRecv(char * buf, int len);
    void SctpSendApplicationData(char * buf, int len, unsigned num);

    virtual void SctpDtlsSend(char * buf, int len) = 0;
    virtual void SctpRecvApplicationData(char * buf, int len) = 0;
    virtual void SctpRecvAck(unsigned num) = 0;
    virtual void SctpRetransmitStart(unsigned to) = 0;
    virtual void SctpRetransmitStop() = 0;
    virtual void SctpHandshakeStart(unsigned to) = 0;
    virtual void SctpHandshakeStop() = 0;
};
