#ifndef CLIENTSOCKET_H
#define CLIENTSOCKET_H

#include <QThread>
#include <QImage>
#include <QSslSocket>
#include <QStringList>
#include <QRect>
#include <QStateMachine>

#include "pixelformat.h"
#include "clientglobals.h"
#include "serverframebufferupdatemessage.h"

class QKeyEvent;
class QWheelEvent;
class QMouseEvent;
class QSize;
class QHostAddress;
class CompressionContext;
class QReadWriteLock;
class QTimer;
class QAudioOutput;
class NativeRawRectReader;

class ClientSocket : public QObject
{
    Q_OBJECT
public:
    ClientSocket( QObject *parent = 0);
    ~ClientSocket();

    void setAuthenticationType( const ClientGlobals::AuthType &type );
    void setPassword( const QByteArray &pass );
    void setUsername( const QByteArray &uname );
    void setSharedSession( bool share );
    void setAudioEnabled( bool enabled );
    void setAudioDevice( QAudioOutput *device );
    void setUseNativeKeys( bool useNativeKeys );
    void setEncodingPriorites( const QStringList &encodings );
    void setCaCertPath( const QString &path );
    void setAuthSubtype( const ClientGlobals::SubAuthType &type );

    const QImage *aquireImage() const;

public slots:
    void connectToHost( const QHostAddress &hostName, quint16 port );
    void disconnectFromHost( );
    void sendKeyEvent( const KeyEvent &event );
    void sendNativeKeyEvent( const KeyEvent &event );
    void sendMouseEvent( const MouseEvent &event );
    void sendUpdateRect( const QRect &rect );
    void sendEnableAudio( );
    void sendDisableAudio( );

signals:
    void framebufferResize( const QSize &size );
    void rectDirty( const QRect &rect );
    void framebufferUpdate( );
    void connected( );
    void disconnected( );
    void error( const QString &message );
    void enableNativeKeys();
    void enableRelativePointer();
    void enableAudio();
    void startAudioChannel();
    void stopAudioChannel();
    void audioData( const QByteArray &data );
    void desktopNameChanged( const QString &name );
    void sendFramebufferChange( const QRect &rect, const QImage &pixels );

    void versionError();
    void versionSupported();
    void securityTypeSupported();
    void securityTypeNotSupported();
    void useVNCAuth();
    void useNoAuth();
    void useVencryptAuth();
    void skipResponse();
    void authSuccessful();
    void securityError();
    void authError();
    void framebufferError();
    void moreBytesAvailable();
    void gotQemuUpdateMessage();
    void gotRfbUpdateMessage();
    void parseNextRfbUpdateRect();
    void gotDesktopResizeMessage();
    void gotWMViMessage();
    void gotPointerTypeChangeMessage();
    void gotAudioMessage();
    void gotNativeKeysMessage();
    void gotRectRawMessage();
    void gotRectRREMessage();
    void gotRectCoRREMessage();
    void gotRectCopyRectMessage();
    void gotRectHextileMessage();
    void gotRectZlibMessage();
    void finishedReadingMessage();

    void usePlainAuth();
    void useTLS();
    void useTLSVNC();
    void useTLSPlain();
    void useX509();
    void useX509VNC();
    void useX509Plain();

private slots:
    void handleConnect( );
    void handleDisconnect( );
    void handleError( const QAbstractSocket::SocketError &err );
    void handleSslError( const QList<QSslError> & errors );
    void handleEncryptionReady();

    void onDisconnected();
    void onHandshaking();
    void onConnected();
    void onSessionDone();
    void onSocketConnected();
    void onSocketError();
    void onHandshakeReadVersion();
    void onHandshakeSendVersionResponse();
    void onHandshakeVersionError();
    void onHandshakeReadSecurityTypes();
    void onHandshakeSendSecurityType();
    void onHandshakeReadSecurityAuthResult();
    void onHandshakeSecurityError();
    void onHandshakeSendVNCAuth();
    void onHandshakeReadVencryptVersion();
    //void onHandshakeSendVencryptSubauth();
    void onHandshakeSendClientInit();
    void onHandshakeReadFramebufferSetup();
    void onHandshakeFramebufferError();
    void onHandleFramebufferUpdateState();
    void onReadFrameBufferMessageTypeState();
    void onReadQemuUpdateMessageState();
    void onReadRfbUpdateMessageState();
    void onReadRfbUpdateParseNextMessageState();
    void onReadRfbUpdateRectDesktopResizeState();
    void onReadRfbUpdateRectWMViState();
    void onReadRfbUpdateRectPointerTypeChangeState();
    void onReadRfbUpdateRectAudioState();
    void onReadRfbUpdateRectNativeKeysState();
    void onReadRfbUpdateRectRawState();
    void onReadRfbUpdateRectRREState();
    void onReadRfbUpdateRectCoRREState();
    void onReadRfbUpdateRectCopyRectState();
    void onReadRfbUpdateRectHextileState();
    void onReadRfbUpdateRectZlibState();
    void onReadQemuUpdateAudioMessageState();
    void onHandleFramebufferUpdateDoneState();
    void onHandshakeReadVencryptSubauth();
    void onHandshakeDoVencryptAuth();
    void onHandshakeSendVencryptPlainAuth();
    void onHandshakeStartTLS();
    void onHandshakeStartTLSVNC();
    void onHandshakeStartTLSPlain();
    void onHandshakeStartX509();
    void onHandshakeStartX509VNC();
    void onHandshakeStartX509Plain();
    void onHandshakeReadVencryptAuthResult();

private:
  void configureStates();
  void sendClientVersion();
  void sendSecurityType();
  void sendPassword();
  void sendClientInit();
  void sendPixelFormat();
  void sendEncodings();
  void readVersion();
  void readSecurityTypes();
  void readSecurityResult();
  void readChallenge();
  void readAuthResult();
  void readInitFramebuffer();
  void readVencryptVersion();
  void readVencryptSubAuthTypes();
  void readVencryptSubAuthResult();
  void updateFrambufferFromPixelFormat(const QSize &size);
  void setupSSL( QSslSocket::PeerVerifyMode mode );

private:
    QSslSocket *m_connection;
    QImage m_framebuffer;
    QByteArray m_challenge;
    QByteArray m_password;
    QList<ClientGlobals::RfbEncoding> m_encodings;
    QRect m_currentUpdateRect;
    PixelFormat m_pixelFormat;
    ClientGlobals::AuthType m_authType;
    ClientGlobals::SubAuthType m_subAuthType;
    bool m_authenticated;
    ClientGlobals::RfbVersion m_maxClientVersion;
    bool m_shared;
    bool m_canAuthenticate;
    CompressionContext *m_unzip;
    bool m_audioSupported;
    QString m_connectionName;
    uchar *m_fbBits;
    bool m_useNativeKeys;
    QStringList m_encodingPriorites;
    bool m_enableAudio;
    QStateMachine *m_stateMachine;
    ServerFramebufferUpdateMessage m_currentFbMessage;
    QAudioOutput *m_audioDevice;
    QIODevice *m_audioBuffer;
    QString m_caCertPath;
    QByteArray m_username;
    NativeRawRectReader *m_rawRectReadState;
};

#endif // CLIENTSOCKET_H

