/*
 * Deskflow -- mouse and keyboard sharing utility
 * Copyright (C) 2013-2016 Symless Ltd.
 *
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 *
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "base/Event.h"

class IEventQueue;

class EventTypes {
public:
  EventTypes();
  void setEvents(IEventQueue *events);

protected:
  IEventQueue *getEvents() const;

private:
  IEventQueue *m_events;
};

#define REGISTER_EVENT(type_, name_)                                           \
  Event::Type type_##Events::name_() {                                         \
    return getEvents()->registerTypeOnce(m_##name_, __FUNCTION__);             \
  }

class ClientEvents : public EventTypes {
public:
  //! @name accessors
  //@{

  //! Get connected event type
  /*!
  Returns the connected event type.  This is sent when the client has
  successfully connected to the server.
  */
  Event::Type connected();

  //! Get connection failed event type
  /*!
  Returns the connection failed event type.  This is sent when the
  server fails for some reason.  The event data is a FailInfo*.
  */
  Event::Type connectionFailed();

  //! Get connection refused event type
  /*!
  Returns the connection refused event type.  This is sent when the
  server refuses for some reason.  The event data is a FailInfo*.
  */
  Event::Type connectionRefused();

  //! Get disconnected event type
  /*!
  Returns the disconnected event type.  This is sent when the client
  has disconnected from the server (and only after having successfully
  connected).
  */
  Event::Type disconnected();

  //@}

private:
  Event::Type m_connected = Event::kUnknown;
  Event::Type m_connectionFailed = Event::kUnknown;
  Event::Type m_connectionRefused = Event::kUnknown;
  Event::Type m_disconnected = Event::kUnknown;
};

class IStreamEvents : public EventTypes {
public:
  IStreamEvents()
      : m_inputReady(Event::kUnknown),
        m_outputFlushed(Event::kUnknown),
        m_outputError(Event::kUnknown),
        m_inputShutdown(Event::kUnknown),
        m_outputShutdown(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get input ready event type
  /*!
  Returns the input ready event type.  A stream sends this event
  when \c read() will return with data.
  */
  Event::Type inputReady();

  //! Get output flushed event type
  /*!
  Returns the output flushed event type.  A stream sends this event
  when the output buffer has been flushed.  If there have been no
  writes since the event was posted, calling \c shutdownOutput() or
  \c close() will not discard any data and \c flush() will return
  immediately.
  */
  Event::Type outputFlushed();

  //! Get output error event type
  /*!
  Returns the output error event type.  A stream sends this event
  when a write has failed.
  */
  Event::Type outputError();

  //! Get input shutdown event type
  /*!
  Returns the input shutdown event type.  This is sent when the
  input side of the stream has shutdown.  When the input has
  shutdown, no more data will ever be available to read.
  */
  Event::Type inputShutdown();

  //! Get output shutdown event type
  /*!
  Returns the output shutdown event type.  This is sent when the
  output side of the stream has shutdown.  When the output has
  shutdown, no more data can ever be written to the stream.  Any
  attempt to do so will generate a output error event.
  */
  Event::Type outputShutdown();

  //@}

private:
  Event::Type m_inputReady;
  Event::Type m_outputFlushed;
  Event::Type m_outputError;
  Event::Type m_inputShutdown;
  Event::Type m_outputShutdown;
};

class IpcClientEvents : public EventTypes {
public:
  IpcClientEvents()
      : m_connected(Event::kUnknown),
        m_messageReceived(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Raised when the socket is connected.
  Event::Type connected();

  //! Raised when a message is received.
  Event::Type messageReceived();

  //@}

private:
  Event::Type m_connected;
  Event::Type m_messageReceived;
};

class IpcClientProxyEvents : public EventTypes {
public:
  IpcClientProxyEvents()
      : m_messageReceived(Event::kUnknown),
        m_disconnected(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Raised when the server receives a message from a client.
  Event::Type messageReceived();

  //! Raised when the client disconnects from the server.
  Event::Type disconnected();

  //@}

private:
  Event::Type m_messageReceived;
  Event::Type m_disconnected;
};

class IpcServerEvents : public EventTypes {
public:
  IpcServerEvents()
      : m_clientConnected(Event::kUnknown),
        m_messageReceived(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Raised when we have created the client proxy.
  Event::Type clientConnected();

  //! Raised when a message is received through a client proxy.
  Event::Type messageReceived();

  //@}

private:
  Event::Type m_clientConnected;
  Event::Type m_messageReceived;
};

class IpcServerProxyEvents : public EventTypes {
public:
  IpcServerProxyEvents() : m_messageReceived(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Raised when the client receives a message from the server.
  Event::Type messageReceived();

  //@}

private:
  Event::Type m_messageReceived;
};

class IDataSocketEvents : public EventTypes {
public:
  IDataSocketEvents()
      : m_connected(Event::kUnknown),
        m_secureConnected(Event::kUnknown),
        m_connectionFailed(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get connected event type
  /*!
  Returns the socket connected event type.  A socket sends this
  event when a remote connection has been established.
  */
  Event::Type connected();

  //! Get secure connected event type
  /*!
   Returns the secure socket connected event type.  A secure socket sends
   this event when a remote connection has been established.
   */
  Event::Type secureConnected();

  //! Get connection failed event type
  /*!
  Returns the socket connection failed event type.  A socket sends
  this event when an attempt to connect to a remote port has failed.
  The data is a pointer to a ConnectionFailedInfo.
  */
  Event::Type connectionFailed();

  //@}

private:
  Event::Type m_connected;
  Event::Type m_secureConnected;
  Event::Type m_connectionFailed;
};

class IListenSocketEvents : public EventTypes {
public:
  IListenSocketEvents() : m_connecting(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get connecting event type
  /*!
  Returns the socket connecting event type.  A socket sends this
  event when a remote connection is waiting to be accepted.
  */
  Event::Type connecting();

  //@}

private:
  Event::Type m_connecting;
};

class ISocketEvents : public EventTypes {
public:
  ISocketEvents()
      : m_disconnected(Event::kUnknown),
        m_stopRetry(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get disconnected event type
  /*!
  Returns the socket disconnected event type.  A socket sends this
  event when the remote side of the socket has disconnected or
  shutdown both input and output.
  */
  Event::Type disconnected();

  //! Get stop retry event type
  /*!
   Returns the stop retry event type.  This is sent when the client
   doesn't want to reconnect after it disconnects from the server.
   */
  Event::Type stopRetry();

  //@}

private:
  Event::Type m_disconnected;
  Event::Type m_stopRetry;
};

class OSXScreenEvents : public EventTypes {
public:
  OSXScreenEvents() : m_confirmSleep(Event::kUnknown) {}

  //! @name accessors
  //@{

  Event::Type confirmSleep();

  //@}

private:
  Event::Type m_confirmSleep;
};

class ClientListenerEvents : public EventTypes {
public:
  ClientListenerEvents()
      : m_accepted(Event::kUnknown),
        m_connected(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get accepted event type
  /*!
   Returns the accepted event type.  This is sent whenever a server
   accepts a client.
   */
  Event::Type accepted();

  //! Get connected event type
  /*!
  Returns the connected event type.  This is sent whenever a
  a client connects.
  */
  Event::Type connected();

  //@}

private:
  Event::Type m_accepted;
  Event::Type m_connected;
};

class ClientProxyEvents : public EventTypes {
public:
  ClientProxyEvents()
      : m_ready(Event::kUnknown),
        m_disconnected(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get ready event type
  /*!
  Returns the ready event type.  This is sent when the client has
  completed the initial handshake.  Until it is sent, the client is
  not fully connected.
  */
  Event::Type ready();

  //! Get disconnect event type
  /*!
  Returns the disconnect event type.  This is sent when the client
  disconnects or is disconnected.  The target is getEventTarget().
  */
  Event::Type disconnected();

  //@}

private:
  Event::Type m_ready;
  Event::Type m_disconnected;
};

class ClientProxyUnknownEvents : public EventTypes {
public:
  ClientProxyUnknownEvents()
      : m_success(Event::kUnknown),
        m_failure(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get success event type
  /*!
  Returns the success event type.  This is sent when the client has
  correctly responded to the hello message.  The target is this.
  */
  Event::Type success();

  //! Get failure event type
  /*!
  Returns the failure event type.  This is sent when a client fails
  to correctly respond to the hello message.  The target is this.
  */
  Event::Type failure();

  //@}

private:
  Event::Type m_success;
  Event::Type m_failure;
};

class ServerEvents : public EventTypes {
public:
  ServerEvents()
      : m_error(Event::kUnknown),
        m_connected(Event::kUnknown),
        m_disconnected(Event::kUnknown),
        m_switchToScreen(Event::kUnknown),
        m_switchInDirection(Event::kUnknown),
        m_keyboardBroadcast(Event::kUnknown),
        m_lockCursorToScreen(Event::kUnknown),
        m_screenSwitched(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get error event type
  /*!
  Returns the error event type.  This is sent when the server fails
  for some reason.
  */
  Event::Type error();

  //! Get connected event type
  /*!
  Returns the connected event type.  This is sent when a client screen
  has connected.  The event data is a \c ScreenConnectedInfo* that
  indicates the connected screen.
  */
  Event::Type connected();

  //! Get disconnected event type
  /*!
  Returns the disconnected event type.  This is sent when all the
  clients have disconnected.
  */
  Event::Type disconnected();

  //! Get switch to screen event type
  /*!
  Returns the switch to screen event type.  The server responds to this
  by switching screens.  The event data is a \c SwitchToScreenInfo*
  that indicates the target screen.
  */
  Event::Type switchToScreen();

  //! Get switch in direction event type
  /*!
  Returns the switch in direction event type.  The server responds to this
  by switching screens.  The event data is a \c SwitchInDirectionInfo*
  that indicates the target direction.
  */
  Event::Type switchInDirection();

  //! Get keyboard broadcast event type
  /*!
  Returns the keyboard broadcast event type.  The server responds
  to this by turning on keyboard broadcasting or turning it off.  The
  event data is a \c KeyboardBroadcastInfo*.
  */
  Event::Type keyboardBroadcast();

  //! Get lock cursor event type
  /*!
  Returns the lock cursor event type.  The server responds to this
  by locking the cursor to the active screen or unlocking it.  The
  event data is a \c LockCursorToScreenInfo*.
  */
  Event::Type lockCursorToScreen();

  //! Get screen switched event type
  /*!
  Returns the screen switched event type.  This is raised when the
  screen has been switched to a client.
  */
  Event::Type screenSwitched();

  //@}

private:
  Event::Type m_error;
  Event::Type m_connected;
  Event::Type m_disconnected;
  Event::Type m_switchToScreen;
  Event::Type m_switchInDirection;
  Event::Type m_keyboardBroadcast;
  Event::Type m_lockCursorToScreen;
  Event::Type m_screenSwitched;
};

class ServerAppEvents : public EventTypes {
public:
  ServerAppEvents()
      : m_reloadConfig(Event::kUnknown),
        m_forceReconnect(Event::kUnknown),
        m_resetServer(Event::kUnknown) {}

  //! @name accessors
  //@{

  Event::Type reloadConfig();
  Event::Type forceReconnect();
  Event::Type resetServer();

  //@}

private:
  Event::Type m_reloadConfig;
  Event::Type m_forceReconnect;
  Event::Type m_resetServer;
};

class IKeyStateEvents : public EventTypes {
public:
  IKeyStateEvents()
      : m_keyDown(Event::kUnknown),
        m_keyUp(Event::kUnknown),
        m_keyRepeat(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get key down event type.  Event data is KeyInfo*, count == 1.
  Event::Type keyDown();

  //! Get key up event type.  Event data is KeyInfo*, count == 1.
  Event::Type keyUp();

  //! Get key repeat event type.  Event data is KeyInfo*.
  Event::Type keyRepeat();

  //@}

private:
  Event::Type m_keyDown;
  Event::Type m_keyUp;
  Event::Type m_keyRepeat;
};

class IPrimaryScreenEvents : public EventTypes {
public:
  IPrimaryScreenEvents()
      : m_buttonDown(Event::kUnknown),
        m_buttonUp(Event::kUnknown),
        m_motionOnPrimary(Event::kUnknown),
        m_motionOnSecondary(Event::kUnknown),
        m_wheel(Event::kUnknown),
        m_screensaverActivated(Event::kUnknown),
        m_screensaverDeactivated(Event::kUnknown),
        m_hotKeyDown(Event::kUnknown),
        m_hotKeyUp(Event::kUnknown),
        m_fakeInputBegin(Event::kUnknown),
        m_fakeInputEnd(Event::kUnknown) {}

  //! @name accessors
  //@{

  //!  button down event type.  Event data is ButtonInfo*.
  Event::Type buttonDown();

  //!  button up event type.  Event data is ButtonInfo*.
  Event::Type buttonUp();

  //!  mouse motion on the primary screen event type
  /*!
  Event data is MotionInfo* and the values are an absolute position.
  */
  Event::Type motionOnPrimary();

  //!  mouse motion on a secondary screen event type
  /*!
  Event data is MotionInfo* and the values are motion deltas not
  absolute coordinates.
  */
  Event::Type motionOnSecondary();

  //!  mouse wheel event type.  Event data is WheelInfo*.
  Event::Type wheel();

  //!  screensaver activated event type
  Event::Type screensaverActivated();

  //!  screensaver deactivated event type
  Event::Type screensaverDeactivated();

  //!  hot key down event type.  Event data is HotKeyInfo*.
  Event::Type hotKeyDown();

  //!  hot key up event type.  Event data is HotKeyInfo*.
  Event::Type hotKeyUp();

  //!  start of fake input event type
  Event::Type fakeInputBegin();

  //!  end of fake input event type
  Event::Type fakeInputEnd();

  //@}

private:
  Event::Type m_buttonDown;
  Event::Type m_buttonUp;
  Event::Type m_motionOnPrimary;
  Event::Type m_motionOnSecondary;
  Event::Type m_wheel;
  Event::Type m_screensaverActivated;
  Event::Type m_screensaverDeactivated;
  Event::Type m_hotKeyDown;
  Event::Type m_hotKeyUp;
  Event::Type m_fakeInputBegin;
  Event::Type m_fakeInputEnd;
};

class IScreenEvents : public EventTypes {
public:
  IScreenEvents()
      : m_error(Event::kUnknown),
        m_shapeChanged(Event::kUnknown),
        m_suspend(Event::kUnknown),
        m_resume(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get error event type
  /*!
  Returns the error event type.  This is sent whenever the screen has
  failed for some reason (e.g. the X Windows server died).
  */
  Event::Type error();

  //! Get shape changed event type
  /*!
  Returns the shape changed event type.  This is sent whenever the
  screen's shape changes.
  */
  Event::Type shapeChanged();

  //! Get suspend event type
  /*!
  Returns the suspend event type. This is sent whenever the system goes
  to sleep or a user session is deactivated (fast user switching).
  */
  Event::Type suspend();

  //! Get resume event type
  /*!
  Returns the resume event type. This is sent whenever the system wakes
  up or a user session is activated (fast user switching).
  */
  Event::Type resume();

  //@}

private:
  Event::Type m_error;
  Event::Type m_shapeChanged;
  Event::Type m_suspend;
  Event::Type m_resume;
};

class ClipboardEvents : public EventTypes {
public:
  ClipboardEvents()
      : m_clipboardGrabbed(Event::kUnknown),
        m_clipboardChanged(Event::kUnknown),
        m_clipboardSending(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get clipboard grabbed event type
  /*!
  Returns the clipboard grabbed event type.  This is sent whenever the
  clipboard is grabbed by some other application so we don't own it
  anymore.  The data is a pointer to a ClipboardInfo.
  */
  Event::Type clipboardGrabbed();

  //! Get clipboard changed event type
  /*!
  Returns the clipboard changed event type.  This is sent whenever the
  contents of the clipboard has changed.  The data is a pointer to a
  IScreen::ClipboardInfo.
  */
  Event::Type clipboardChanged();

  //! Clipboard sending event type
  /*!
  Returns the clipboard sending event type. This is used to send
  clipboard chunks.
  */
  Event::Type clipboardSending();

  //@}

private:
  Event::Type m_clipboardGrabbed;
  Event::Type m_clipboardChanged;
  Event::Type m_clipboardSending;
};

class FileEvents : public EventTypes {
public:
  FileEvents()
      : m_fileChunkSending(Event::kUnknown),
        m_fileRecieveCompleted(Event::kUnknown),
        m_keepAlive(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Sending a file chunk
  Event::Type fileChunkSending();

  //! Completed receiving a file
  Event::Type fileRecieveCompleted();

  //! Send a keep alive
  Event::Type keepAlive();

  //@}

private:
  Event::Type m_fileChunkSending;
  Event::Type m_fileRecieveCompleted;
  Event::Type m_keepAlive;
};

class EiEvents : public EventTypes {
public:
  EiEvents() : m_connected(Event::kUnknown), m_sessionClosed(Event::kUnknown) {}

  //! @name accessors
  //@{

  //! Get connected event type
  /*!
  This event is sent whenever connection to EIS is established and a file
  descriptor for reading events is available.
  */
  Event::Type connected();

  //! Get session closed event type
  /*!
  This event is sent whenever the portal session managing our EIS connection
  is closed.
  */
  Event::Type sessionClosed();

  //@}

private:
  Event::Type m_connected;
  Event::Type m_sessionClosed;
};
