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

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

class ImageUpdateBlock : public istd::listElement<ImageUpdateBlock> {
public:
    ImageUpdateBlock(class IImage *img) { 
        this->img = img; 
    };
    ~ImageUpdateBlock() {
        delete this->img; 
    };

    class IImage *img;
};

class ScreenBufferAppBlock {
    istd::list<class ImageUpdateBlock> updates;

public:
    ScreenBufferAppBlock(int x, int y) {
        this->x = x;
        this->y = y;
        this->version = 0;
        this->update = 0;
    };
    ~ScreenBufferAppBlock() {
        DeleteUpdates();
    };

    void DeleteUpdates() {
        if (!this->updates.empty()) {
            while (this->updates.front()) {
                delete this->updates.front();
            }
        }
    }

    int x;
    int y;
    byte version;
    byte update;

    bool AddImageUpdate(class IImage * image);
    class IImage * GetImageUpdate(byte update);
};

class ScreenSinkHelper : public istd::listElement<ScreenSinkHelper> {
public:
    ScreenSinkHelper(class IScreenSink * sink) {
        this->sink = sink;
    };
    ~ScreenSinkHelper() {};

    class IScreenSink * sink;
};

class ScreenBuffer : public IScreenBuffer {
    class IInstanceLog * const log;

    istd::list<class ScreenSinkHelper> sinks;
    class ScreenBufferAppBlock * blocks[MAX_X_BLOCKS][MAX_Y_BLOCKS];  // Ultra HD
    int w;
    int h;

    class ILowQualityImage * lowQualityImage;

    bool AddImageUpdate(class IImage * image);
    void CleanScreenBufferAppBlocks();

public:
    ScreenBuffer(class IInstanceLog * const log);
    ~ScreenBuffer();

    // Stores a new version of a block or an update of an existing block
    // Called by an image source (capture, unreliable network transmission)
    // Drop blocks and updates with an older version
    // Queue updates that need the block with the right version or previous updates
    // Only trigger calls in IScreenSink for the consistent part of the current block

    //void Set(byte x, byte y, class IImage * image, byte version, byte update) override;
    void Set(class IImage * image) override;
    void Set(class ILowQualityImage * image) override;

    // Returns the number of updates of the specified block
    // Called by IScreenSink to get the image for displaying or network transmission
    byte GetNumUpdates(byte x, byte y) override;

    // Returns the current version of the specified block
    // Called by IScreenSink to get the image for displaying or network transmission
    class IImage * Get(byte x, byte y) override;
    class ILowQualityImage * GetLowQuality() override;

    // Returns the specified update of the specified block
    // Called by IScreenSink to get the image for displaying or network transmission
    class IImage * GetUpdate(byte x, byte y, byte update) override;

    void UpdateAppResolution(int w, int h) override;

    void RegisterScreenSink(class IScreenSink * sink) override;
    void UnregisterScreenSink(class IScreenSink * sink) override;

    // Clear ScreenBuffer. 
    // LowQualityImage arrived
    void Clear() override;
};
