/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2013 Synergy Si 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:
	ClientEvents() :
		m_connected(Event::kUnknown),
		m_connectionFailed(Event::kUnknown),
		m_disconnected(Event::kUnknown) { }

	//! @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 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::Type		m_connectionFailed;
	Event::Type		m_disconnected;
};

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_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 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_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_connected(Event::kUnknown) { }

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

	//@}

private:
	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;
};
