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

class AppSharingContainerHelper : public istd::listElement<AppSharingContainerHelper> {
public:
    AppSharingContainerHelper(void * session, void * container, const char * containerId, enum MediaType type, bool local) {
        this->containerId = _strdup(containerId);
        this->type = type;
        this->container = container;
        this->session = session;
        this->local = local;
        this->conference = false;
    };
    ~AppSharingContainerHelper() {
        if(this->containerId) free(this->containerId);
    }

    void * session;
    void * container;
    enum MediaType type;
    char * containerId;
    bool local;
    bool conference;
};

class Application : public istd::listElement<class Application> {
public:
    Application(unsigned int id, byte sessionId, const char * name, const char * description, const char* hwId, const char * icon,  const char * thumbnail, void * handle, void * context, bool isDesktop) {
        this->id = id;
        this->handle = handle;
        this->icon = _strdup(icon);
        this->thumbnail = _strdup(thumbnail);
        this->captureTimer = NULL;
        this->sentData = 0;
        this->sessionId = sessionId;
        this->context = context;
        this->isDesktop = isDesktop;

        this->wcName = (word *)malloc((strlen(name)+1)<<1);
        memset(this->wcName, 0x0, (strlen(name)+1)<<1);
        str::latin1_to_ucs2_n(name, (word *)this->wcName, strlen(name)+1);
        this->name = _strdup(name);

        this->wcDescription = (word *)malloc((strlen(description)+1)<<1);
        memset(this->wcDescription, 0x0, (strlen(description)+1)<<1);
        str::latin1_to_ucs2_n(description, this->wcDescription, strlen(description)+1);
        this->description = _strdup(description);
        this->screenBuffer = NULL;

        this->hwId = _strdup(hwId);
        this->adminCapture = false;
    };
    ~Application() {
        if(this->wcName) free(this->wcName);
        if(this->name) free(this->name);
        if(this->wcDescription) free(this->wcDescription);
        if(this->description) free(this->description);
        if(this->hwId) free(this->hwId);
        if(this->icon) free(this->icon);
        if(this->thumbnail) free(this->thumbnail);
        if(captureTimer) delete captureTimer;
        if(this->screenBuffer) delete this->screenBuffer;
    };

    void Update(unsigned int id, const char * name, const char * description, const char * hwId, const char * icon,  const char * thumbnail) {
        if(this->wcName) free(this->wcName);
        this->wcName = (word *)malloc((strlen(name)+1)<<1);
        memset(this->wcName, 0x0, (strlen(name)+1)<<1);
        str::latin1_to_ucs2_n(name, this->wcName, strlen(name)+1);
        if(this->name) free(this->name);
        this->name = _strdup(name);

        if(this->wcDescription) free(this->wcDescription);
        this->wcDescription = (word *)malloc((strlen(description)+1)<<1);
        memset(this->wcDescription, 0x0, (strlen(description)+1)<<1);
        str::latin1_to_ucs2_n(description, this->wcDescription, strlen(description)+1);
        if(this->description) free(this->description);
        this->description = _strdup(description);

        if(this->hwId) free(this->hwId);
        this->hwId = _strdup(hwId);

        if(this->icon) free(this->icon);
        this->icon = _strdup(icon);
        if(this->thumbnail) free(this->thumbnail);
        this->thumbnail = _strdup(thumbnail);
    }

    unsigned int id;
    byte sessionId;
    void * context;
    int sentData;
    void * handle;
    char * name;
    char * description;
    char * hwId;
    word * wcName;
    word * wcDescription;
    char * icon;
    char * thumbnail;
    class ITimer * captureTimer;
    ulong64 tvStart;
    class IScreenBuffer * screenBuffer;
    enum AppSharingCoder coder;
    bool isDesktop;
    bool adminCapture;
};

enum session_state {
    Session_connecting,
    Session_connected,
    Session_closing
};

class InfoMessage : public istd::listElement<class InfoMessage> {
    byte seq;
    byte sessionId;
    unsigned num;
    char * data;
    int len;
    int cnt;
public:
    InfoMessage(char * data, int len, byte sessionId, byte seq, unsigned num) {
        this->data = data;
        this->len = len;
        this->sessionId = sessionId;
        this->seq = seq;
        this->num = num;
        this->cnt = 0;
    }
    ~InfoMessage() {
        if(this->data) free(this->data);
    };

    char * get_data() { return this->data; };
    int get_len() { return this->len; };
    byte get_seq() { return this->seq; };
    byte get_session_id() { return this->sessionId; };
    unsigned get_num() { return this->num; };
    void increase_counter() { this->cnt++; };
    int get_counter() { return this->cnt; };
};

class InfoKeyboardMessage : public istd::listElement<class InfoKeyboardMessage> {
    word seq;
    byte sessionId;
    char* data;
    int len;
public:
    InfoKeyboardMessage(char* data, int len, byte sessionId, word seq) {
        this->data = data;
        this->len = len;
        this->seq = seq;
        this->sessionId = sessionId;
    }
    ~InfoKeyboardMessage() {
        if (this->data) free(this->data);
    };

    char* get_data() { return this->data; };
    int get_len() { return this->len; };
    word get_seq() { return this->seq; };
    byte get_session_id() { return this->sessionId; };
};

class BlockMessage : public istd::listElement<class BlockMessage> {
public:
    BlockMessage(word seq, int posX, int posY, int dimX, int dimY) {
        this->seq = seq;
        this->posX = posX;
        this->posY = posY;
        this->dimX = dimX;
        this->dimY = dimY;
    }
    ~BlockMessage() {};

    word seq;
    int posX;
    int posY;
    int dimX;
    int dimY;
};

class SharingSession : public istd::listElement<class SharingSession> {
    byte sharing_session_id;
    class IScreenBuffer * screenBuffer;
    class AppSharingIoChannel * sharing_channel;
    char * appName;
    char * appDesc;
    char * senderName;
    word seq;
    byte infoSeq;
    istd::list<class InfoMessage> info;
public:
    SharingSession(class AppSharingIoChannel * sharing_channel, class IInstanceLog * const log, byte sharing_session_id, const char * appName, const char * appDesc, const char * senderName) {
        this->sharing_session_id = sharing_session_id;
        this->sharing_channel = sharing_channel;
        this->screenBuffer = IScreenBuffer::Create(log);
        this->seq = 0;
        this->infoSeq = 0;
        this->appName = _strdup(appName);
        this->appDesc = _strdup(appDesc);
        this->senderName = _strdup(senderName);
    };
    ~SharingSession() {
        delete this->screenBuffer;
        if(this->appName) free(this->appName);
        if(this->appDesc) free(this->appDesc);
        if(this->senderName) free(this->senderName);
        if(!this->info.empty()) {
            while(this->info.front()) {
                delete this->info.front();
            }
        }
    };

    class AppSharingIoChannel * get_sharing_channel() { return this->sharing_channel; };
    word get_next_sequence() { return this->seq++; };
    byte get_sharing_session_id() { return this->sharing_session_id; };
    class IScreenBuffer * get_screen_buffer() { return this->screenBuffer; };
    void delete_image() { this->screenBuffer->Clear(); }
    void add_image(class IImage * img) { this->screenBuffer->Set(img); }
    void add_low_image(class ILowQualityImage * img) { this->screenBuffer->Set(img); }
    const char * get_app_name() { return this->appName; };
    const char * get_app_desc() { return this->appDesc; };
    const char * get_sender_name() { return this->senderName; };
    void register_sink(class IScreenSink * sink) { this->screenBuffer->RegisterScreenSink(sink); };
    void unregister_sink(class IScreenSink * sink) { this->screenBuffer->UnregisterScreenSink(sink); };
    void update_app_resolution(int w, int h) { this->screenBuffer->UpdateAppResolution(w, h); };
    byte get_info_seq() { return this->infoSeq++; };
    void add_info_message(class InfoMessage * msg) { this->info.push_back(msg); };
    class InfoMessage * get_info_message() {
        if(!this->info.empty()) return this->info.front();
        else return NULL;
    };
    void remove_info_message(byte session_id, byte seq) {
        if(!this->info.empty()) {
            for(class InfoMessage * i = this->info.front(); i; i = i->goNext()) {
                if(i->get_session_id() == session_id && i->get_seq() == seq) {
                    debug->printf("SharingSession(%p)::remove_info_message session_id=%i, seq=%i", this, session_id, seq);
                    i->remove();
                    delete i;
                    return;
                }
            }
        }
    };
};

class ForwardingSession : public istd::listElement<class ForwardingSession> {
    class SharingSession * sharing_session;
    class AppSharingIoChannel * sharing_channel;
    class AppSharingIoChannel * forwarding_channel;
    class UScreenSink * user;
    byte sharing_session_id;
    byte remote_session_id;
    enum session_state state;
    int initSessionCounter;
    class IScreenSink * sink;
    char * appName;
    char * appDesc;
    char * senderName;
    word seq;
    char * participant;
    byte infoSeq;
    istd::list<class InfoMessage> info;
public:
    ForwardingSession(class IIoMux * const iomux, class UScreenSink * const user, class SharingSession * sharing_session, class AppSharingIoChannel * sharing_channel, class AppSharingIoChannel * forwarding_channel, byte remote_session_id, int bitrate, int waitingMsForAck, const char * appName, const char * appDesc, const char * senderName, class IInstanceLog * const log) {
        debug->printf("ForwardingSession(%p)::ForwardingSession sharing_session=%d remote_session=%d", this, sharing_session->get_sharing_session_id(), remote_session_id);
        this->sharing_session = sharing_session;
        this->sharing_channel = sharing_channel;
        this->forwarding_channel = forwarding_channel;
        this->user = user;
        this->sharing_session_id = sharing_session->get_sharing_session_id();
        this->remote_session_id = remote_session_id;
        this->state = Session_connecting;
        this->initSessionCounter = 0;
        this->seq = 0;
        this->participant = NULL;
        this->infoSeq = 0;
        this->appName = _strdup(appName);
        this->appDesc = _strdup(appDesc);
        this->senderName = _strdup(senderName);
        this->sink = IScreenSink::Create(iomux, user, sharing_session->get_screen_buffer(), remote_session_id, bitrate, waitingMsForAck, appName, appDesc, senderName, log);
        sharing_session->register_sink(this->sink);
    };
    ~ForwardingSession() {
        if(this->sink) delete this->sink;
        if(this->appName) free(this->appName);
        if(this->appDesc) free(this->appDesc);
        if(this->senderName) free(this->senderName);
        if(this->participant) free(this->participant);
    };

    word get_next_sequence() { return this->seq++; };
    class SharingSession * get_sharing_session() { return this->sharing_session; };
    class AppSharingIoChannel * get_sharing_channel() { return this->sharing_channel; };
    class AppSharingIoChannel * get_forwarding_channel() { return this->forwarding_channel; };
    class UScreenSink * get_user_sink() { return this->user; };
    byte get_sharing_session_id() { return this->sharing_session_id; };
    byte get_remote_session_id() { return this->remote_session_id; };
    enum session_state get_session_state() { return this->state; };
    void set_session_state(enum session_state state) { this->state = state; };
    void increase_counter() { this->initSessionCounter++; };
    void reset_counter() { this->initSessionCounter = 0; };
    int get_counter() { return this->initSessionCounter; };
    void recv_ack(unsigned num) { this->sink->RecvAck(num); };
    void recv_sink(char * buf, int len) { this->sink->Recv(buf, len); };
    void unregister_sink() {
        this->sink->Stop();
        if(this->sharing_session) this->sharing_session->unregister_sink(this->sink);
    }
    void add_participant(char * participant) {
        if(this->participant) free(this->participant);
        this->participant = participant;
    }
    const char * get_participant() { return this->participant; };

    byte get_info_seq() { return this->infoSeq++; };
    void add_info_message(class InfoMessage * msg) { this->info.push_back(msg); };
    class InfoMessage * get_info_message() {
        if(!this->info.empty()) return this->info.front();
        else return NULL;
    };
    void remove_info_message(byte session_id, byte seq) {
        if(!this->info.empty()) {
            for(class InfoMessage * i = this->info.front(); i; i = i->goNext()) {
                if(i->get_session_id() == session_id && i->get_seq() == seq) {
                    debug->printf("ForwardingSession(%p)::remove_info_message session_id=%i, seq=%i", this, session_id, seq);
                    i->remove();
                    delete i;
                    return;
                }
            }
        }
    };
};

class AppSharingIo : public IAppSharingIo, public UImageBuffer, public UTimer {
    class IInstanceLog * const log;
    class UAppSharingIo * appSharingIoUser;
    class UDeviceIo * deviceUser;
    class IIoMux * const iomux;

    class ITimer * infoTimer;
    class ITimer * forwardingTimer;
    class ITimer * deleteTimer;

    istd::list<class AppSharingIoChannel> ioChannels;
    unsigned short seq;
    int userId;
    char *displayname;
    word *wdisplayname;
    byte sessionId;

    istd::list<class AppSharingContainerHelper> containers;
    istd::list<class Application> apps;
    istd::list<class Application> shareApps;

    istd::list<class SharingSession> sharingSessions;
    istd::list<class ForwardingSession> forwardingSessions;
    istd::list<class ForwardingSession> deletingSessions;

    class MediaConfiguration * mediaConfiguration;

    int execCnt;
    bool shutDown; 
    bool subscribe;

    class IImageBuffer * imageBuffer;

    class Application * FindApplicationById(unsigned int appId);

    void Reset();

public:
    AppSharingIo(class IIoMux * const iomux, class IInstanceLog * const log, class MediaConfiguration * mediaConfiguration);
    ~AppSharingIo();

    void Add(class AppSharingIoChannel * appSharingIoChannel);
    void Remove(class AppSharingIoChannel * appSharingIoChannel);
    void SetDn(const char * dn);
    void SctpRecv(class AppSharingIoChannel * appSharingIoChannel, void * buf, size_t len);
    byte GetSessionId() { return sessionId++; };
    bool GetWindowPosition(byte sessionId, word * x, word * y, int * aspectRatio);
    void ReportBlockLost(unsigned int appId, int posX, int posY, int dimX, int dimY);
    void RequestNewImage(unsigned int appId);
    void ForceCapture(byte sessionId);
    bool IsSharing() { return shareApps.empty() == false; };
    class IImageBuffer* GetImageBuffer() { return this->imageBuffer; };

    void QueryDevices(void * context) override {};
    const char * StartDevice(void * context, void * src, const char * deviceId, int deviceMode) override { return NULL; };
    void StopDevice(void * context, const char * deviceId) override {};
    void SelectDevice(const char *app, const char *deviceId) override {};
    const char * GetDeviceIo(const char * app) override { return NULL; };

    void Initialize(class UAppSharingIo * const user, class UDeviceIo * const deviceIoUser) override;
    void Close() override;
    void SignalingMessage(void * context, void * buffer, int len) override;
    void SubscribeApplications() override;
    void UnsubscribeApplications() override;
    void ShareApplication(void * context, unsigned int id, bool admin) override;
    void UnshareApplication(void * context, unsigned int id) override;
    void GiveControlToUser(void * context, unsigned int sessionId) override;
    void RemoveControlFromUser(void * context, unsigned int sessionId) override;
    void RequestControlFromUser(void * context, unsigned int sessionId) override;
    void AddRemoteContainer(void * context, void * container, const char * channelId, enum MediaType coder);
    void CheckRemoteContainers(void * context, const char * channelId, enum MediaType coder);
    void RemoveRemoteContainer(void * context, enum MediaType type);
    void UnshareAllApplications(void * context) override;

    bool IsPhoneLoad() { return this->mediaConfiguration->phoneLoad; };
    void Add(unsigned int id, const char * name, const char * description, const char * hwId, const char * icon,  const char * thumbnail, void * handle, bool isDesktop) override;
    void Update(unsigned int id, const char * name, const char * description, const char * hwId, const char * icon,  const char * thumbnail) override;
    void Remove(unsigned int id) override;
    void MousePosition(int coorX, int coorY, int type, unsigned int appId) override;
    void UpdateResolution(int dimX, int dimY, unsigned int appId) override;
    void CloseComplete() override;
    void ClipboardUpdate(const char * clipboard, int len) override;
    void IsSecureDesktop(bool secure) override;
    void ReloadApplicationList() override;
    void ForceSendData(int appId) override;

    void Conference(class AppSharingIoChannel * appSharingIoChannel, unsigned char * buf, size_t len);
    void ConferenceSctpRecvAck(class AppSharingIoChannel * appSharingIoChannel, unsigned num);
    int SendRemoteControlData(class AppSharingIoChannel* appSharingIoChannel, int appId);
    void EndSendRemoteControlData(int appId);
    void RxClipboardUpdate(const byte * clipboard, int len);
    void MouseWheel(int delta);
    void MouseButtonDown(enum MOUSE_KEY key);
    void MouseButtonUp(enum MOUSE_KEY key);

    void TimerOnTimeout(ITimer * timer) override;
};

class AppSharingIoChannelSinkSession : public istd::listElement<AppSharingIoChannelSinkSession> {
public:
    AppSharingIoChannelSinkSession(unsigned int appId, byte sessionId, class IScreenSink * sink, class IScreenBuffer * buffer) {
        this->appId = appId;
        this->sessionId = sessionId;
        this->sink = sink;
        this->buffer = buffer;
        this->closing = false;
        this->connected = false;
    };
    ~AppSharingIoChannelSinkSession() {};

    unsigned int appId;
    byte sessionId;
    bool closing;
    bool connected;
    class IScreenSink * sink;
    class IScreenBuffer * buffer;
};

class ClipboardChunk : public istd::listElement<ClipboardChunk> {
public:
    ClipboardChunk(byte * chunk, int len) {
        this->chunk = chunk;
        this->len = len;
    };
    ~ClipboardChunk() { if (this->chunk) free(this->chunk); };

    byte * chunk;
    int len;
};

class IImageHelper : public istd::listElement<IImageHelper> {
public:
    IImageHelper(class IImage* img, unsigned int appId) {
        this->img = img;
        this->appId = appId;
    };
    ~IImageHelper() {};

    class IImage * img;
    unsigned int appId;
};

class AppSharingIoChannel : public UScreenSink, public URemoteControl, public UTimer, public IAppSharingIoChannel, public istd::listElement<AppSharingIoChannel>, public UIoExec {
protected:
    friend class AppSharingIo;
    class AppSharingIo * const appSharingIo;
    class UMediaIoChannel * const user;
    class IInstanceLog * const log;
    class IIoMux * const iomux;
    class IRemoteControl * remoteControl;

    class ITimer * deleteTimer;
    class ITimer * clipboardTimer;
    int clipboardTxId;
    byte * clipboardRx;
    int clipboardRxLen;

    unsigned short seq;

    byte lastInfoMessage;
    char * remoteParticipant;
    char * channelId;
    void * context;
    bool open;
    bool conference;
    enum MediaType type;
    enum AppSharingCoder coder;
   
    bool closed;
    bool closing;

    char * dn;
    byte infoMsgSeq;

    int numTxProcSamples;
    int procTxDataTot;
    int procTxDataPar;
    ulong64 firstTxTimeStamp;
    ulong64 partialTxTimeStamp;

    int numRxProcSamples;
    int procRxDataTot;
    int procRxDataPar;
    ulong64 firstRxTimeStamp;
    ulong64 partialRxTimeStamp;

    dword token;
    int haveControl; 

    class MediaConfiguration * mediaConfiguration;

    istd::list<class RemoteControlSession> remoteControlSessions;

    istd::list<class AppSharingIoChannelSinkSession> sessions;
    istd::list<class InfoMessage> infoMessagesProc;
    class InfoMessage * SearchLastInfoMessage(byte sessionId);
    void DeleteInfoMessages(byte sessionId);

    int rxSessionIds[256];
    int txSessionIds[256];
    int fwdSessionIds[256];

    void RecvPhoneLoad(void * buf, size_t len);
    int lastXCoor;
    int lastYCoor;
    int lastMouseType;

    istd::list<class InfoKeyboardMessage> infoKeyboardMessagesProc;
    bool initLastProcInfoSequence;
    word lastProcInfoSequence;

    istd::list<class IImageHelper> images;

    istd::list<class ClipboardChunk> clipboardChunks;

public:
    AppSharingIoChannel(class IIoMux * const iomux, class IAppSharingIo * mediaIo, class UMediaIoChannel * const user, class IInstanceLog * log, const char * dn, class MediaConfiguration * mediaConfiguration);
    ~AppSharingIoChannel();

    char * GetRemoteParticipant() { return this->remoteParticipant; };
    void SetConference(bool conference) { this->conference = conference; };
    void SignalingMessage(void * buf, int len);
    void ForwardInfoMessage(class ForwardingSession * fs, char * message, int len);
    void SendInfoMessage(class SharingSession * sharingSession, char * message, int len, byte seq, unsigned num);

    // IAppSharingIoChannel
    void Initialize(void * context, const char * channelId, enum AppSharingCoder coder) override;

    // IMediaIoChannel
    void Open() override;
    void RtpRecv(void * buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker) override {};
    void SctpRecv(void * buf, size_t len) override;
    void SctpRecvAck(unsigned num) override;
    void SctpSendResult() override {};
    void Close() override;
    void EventRecv(enum MediaEndpointEvent event, void* ctx) override {};

    // UScreenSink
    void SendSinkMessage(void * buf, size_t len, unsigned num) override;
    void ReceiveMessage(void * buf, size_t len) override;
    void ReportBlockLost(int appId, int posX, int posY, int dimX, int dimY) override;
    void RequestNewImage(int appId) override;

    // UTimer
    void TimerOnTimeout(ITimer * timer) override;

    // URemoteControl
    void MouseWheel(int delta) override;
    void MouseButtonDown(enum MOUSE_KEY key) override;
    void MouseButtonUp(enum MOUSE_KEY key) override;
    void RemoteControlCloseComplete() override;

    // UIoMux
    void IoExec(void* execContext);

    void CreateRemoteControlSession(int appId, byte sessionId, char* appName, char* appDesc, class IScreenBuffer* screenBuffer, int bitrate, int waitMsForAck, const char* senderName);
    void RemoteControlMessageRecv(void* buf, size_t len);
    void RemoteControlUiMessageRecv(void* buf, size_t len);
    void RemoveRemoteControlSession(int appId);
    void SendImageBlocks(unsigned int appId);
    void ImageBlock(class IImage* img, unsigned int appId);
    int SendRemoteControlData(int appId) { return appSharingIo->SendRemoteControlData(this, appId); };
    void EndSendRemoteControlData(int appId) { appSharingIo->EndSendRemoteControlData(appId); };
    bool IsRemoteControlSessionCreated(int appId, byte sessionId);
    void SendClipboardUpdate(const char * clipboard, int len);

    void CreateScreenSink(int appId, byte sessionId, char * appName, char * appDesc, class IScreenBuffer * screenBuffer, int bitrate, int waitMsForAck, const char * senderName);
    void RemoveScreenSink(class IScreenBuffer * screenBuffer);
    const void * GetContext(void) { return this->context; };
    const char * GetChannelId(void) { return this->channelId; };
    int GetCoder(void) { return this->coder; };
    enum MediaType GetMediaType(void) { return this->type; };
    void MousePosition(int coorX, int coorY, int type, unsigned int appId);
    void MouseMove(int coorX, int coorY);
    void UpdateResolution(int dimX, int dimY, unsigned int appId);
    void RegisterForwardingSession(byte sessionId);
    void UnregisterForwardingSession(byte sessionId);
    void IsSecureDesktop(bool secure);

    void StartAdminCapture();
};

#define BITRATE_SLOTS  40
#define BITRATE_TIMER  25

class RemoteControlSession : public UTimer, public istd::listElement<RemoteControlSession> {
    class IInstanceLog* const log;
    class IIoMux* const iomux;
    class AppSharingIoChannel* const channel;
    class MediaConfiguration* mediaConfiguration;

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

    bool resolutionUpdated;
    bool waitUpdateSessionAck;
    int expectedWidth;
    int expectedHeight;
    char* appName;
    char* appDesc;
    char* senderName;
    char* createSession;
    int createSessionLen;

    int w;
    int h;

    void CalculateBitrate(int indexData);

    int totDataSent;
    int maxSendBytes;
    int maxChunkBytes;
    int appSharingBitrate;
    int numPacketLost;
    int bitrateSlots[BITRATE_SLOTS];
    int indexBitrate;
    bool CalculateBitrate();
    class ITimer* bitrateTimer;

    istd::list<class BlockMessage> sentBlocks;
    int numBlockMessages;

public:
    RemoteControlSession(class IIoMux* const iomux, byte appId, byte sessionId, const char* appName, const char* appDesc, const char* senderName, class IInstanceLog* const log, class AppSharingIoChannel* channel, class MediaConfiguration* mediaConfiguration);
    ~RemoteControlSession();

    void ReportBlockLost();
    void UpdateResolution(int w, int h);
    void Recv(void* buf, int len);
    void AddSentBlock(word seq, int posX, int posY, int dimX, int dimY) {
        sentBlocks.push_back(new BlockMessage(seq, posX, posY, dimX, dimY));
        if (numBlockMessages > 1024) delete sentBlocks.pop_front();
        else numBlockMessages++;
    }

    void TimerOnTimeout(ITimer* timer) override;

    word startSequence;
    byte appId;
    byte sessionId;
    bool closing;
    bool sessionCreated;
};