/*---------------------------------------------------------------------------*/
/* screen_sink.h                                                             */
/* copyright (c) innovaphone 2018                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

class ScreenSinkAppBlockCtx {
public:
    ScreenSinkAppBlockCtx(int x, int y) {
        this->x = x;
        this->y = y;
    };
    int x;
    int y;
};

class ImageUpdateSent : public istd::listElement<ImageUpdateSent> {
public:
    ImageUpdateSent(int version, int update) {
        this->version = version;
        this->update = update;
        this->waitingMsAck = 0;
    };
    ~ImageUpdateSent() {};

    int version;
    int update;
    word sequence;
    int waitingMsAck;
};

class ScreenSinkAppBlock {
    istd::list<class ImageUpdateSent> imageUpdateList;
public:
    ScreenSinkAppBlock(int x, int y, int version = 0) {
        this->x = x;
        this->y = y;
        this->version = version; 
    };
    ~ScreenSinkAppBlock() { CleanImageUpdateList(); };

    int x;
    int y;
    int version;

    void CleanImageUpdateList() {
        if(!this->imageUpdateList.empty()) {
            //debug->printf("ScreenSinkAppBlock(%p)::CleanImageUpdateList", this);
            while(this->imageUpdateList.front()) {
                delete this->imageUpdateList.front();
            }
        }
    }

    void AddImage(int version) {
        this->imageUpdateList.push_back(new class ImageUpdateSent(version, 0));
        this->version = version;
    }

    void AddImageUpdate(int update) {
        this->imageUpdateList.push_back(new class ImageUpdateSent(this->version, update));
    }

    class ImageUpdateSent * GetImageUpdate() {
        if(!this->imageUpdateList.empty()) return this->imageUpdateList.front();
        return NULL;
    }
};

#define SESSION_TIMER 100
#define BITRATE_TIMER  25
#define BITRATE_SLOTS  40
#define MAX_SEQUENCE_SINK 1024

class ScreenSink : public IScreenSink, public UTimer, public istd::listElement<ScreenSink> {
    class IInstanceLog * const log;
    class IIoMux * const iomux;
    class IScreenBuffer * const screenBuffer;
    class UScreenSink * const user;

    class ScreenSinkAppBlock * blocks[MAX_X_BLOCKS][MAX_Y_BLOCKS];  // Ultra HD
    int lastX;
    int lastY;

    class ITimer * sessionTimer;
    class ITimer * bitrateTimer;
    unsigned char startSessionMsg[6];
    word seq;

    byte sessionId;
    bool sessionCreated;
    bool waitUpdateSessionAck;
    int expectedWidth;
    int expectedHeight;
    char * appName;
    char * appDesc;
    char * senderName;
    char * createSession;
    int createSessionLen;

    int totalDataSent;
    int totDataSent;
    int maxSendBytes;
    int bitrate;
    int w;
    int h;
    int bitrateSlots[BITRATE_SLOTS];
    int indexBitrate;
    int lastBitrateTimerValue;
    ulong64 lastBitrateTimerStart;
    word sequenceTable[MAX_SEQUENCE_SINK];

    int waitingMsForAck;
    bool closing;

    bool CalculateBitrate();
    void CleanScreenSinkAppBlocks();
    int SendImage(int x, int y);

    char deleteLowQualityImage[2];
    bool waitLowQualityImageAck;
    int numDeleteLowQualityMsgs;

public:
    ScreenSink(class IIoMux * const iomux, class UScreenSink * const user, class IScreenBuffer * const screenBuffer, byte sessionId, int bitrate, int waitingMsForAck, const char * appName, const char * appDesc, const char * senderName, class IInstanceLog * const log);
    ~ScreenSink();

    // Notification that a new version of a block is ready for displaying / transmission
    // Senders should mark this block version in the local state
    void ImageAdded(byte x, byte y, byte version) override;

    void LowQualityImageAdded() override;

    // Notification that a new update of a block is ready for displaying / transmission
    // Senders should mark this update in the local state
    void ImageUpdated(byte x, byte y, byte update) override;

    void UpdateAppResolution(int w, int h) override;
    void Recv(void * buf, int len) override;
    void RecvAck(unsigned num) override;
    void Stop() override;

    void TimerOnTimeout(ITimer * timer) override;
};