/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2012 Synergy Si Ltd.
 * Copyright (C) 2005 Chris Schoeneman
 * 
 * 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 "synergy/key_types.h"
#include "synergy/mouse_types.h"
#include "synergy/protocol_types.h"
#include "synergy/IPlatformScreen.h"
#include "base/String.h"
#include "common/stdmap.h"
#include "common/stdset.h"

class PrimaryClient;
class Event;
class IEventQueue;

class InputFilter {
public:
	// -------------------------------------------------------------------------
	// Input Filter Condition Classes
	// -------------------------------------------------------------------------
	enum EFilterStatus {
		kNoMatch,
		kActivate,
		kDeactivate
	};

	class Condition {
	public:
		Condition();
		virtual ~Condition();

		virtual Condition*		clone() const = 0;
		virtual String			format() const = 0;

		virtual EFilterStatus	match(const Event&) = 0;

		virtual void			enablePrimary(PrimaryClient*);
		virtual void			disablePrimary(PrimaryClient*);
	};
	
	// KeystrokeCondition
	class KeystrokeCondition : public Condition {
	public:
		KeystrokeCondition(IEventQueue* events, IPlatformScreen::KeyInfo*);
		KeystrokeCondition(IEventQueue* events, KeyID key, KeyModifierMask mask);
		virtual ~KeystrokeCondition();

		KeyID					getKey() const;
		KeyModifierMask			getMask() const;

		// Condition overrides
		virtual Condition*		clone() const;
		virtual String			format() const;
		virtual EFilterStatus	match(const Event&);
		virtual void			enablePrimary(PrimaryClient*);
		virtual void			disablePrimary(PrimaryClient*);

	private:
		UInt32					m_id;
		KeyID					m_key;
		KeyModifierMask			m_mask;
		IEventQueue*			m_events;
	};

	// MouseButtonCondition
	class MouseButtonCondition : public Condition {
	public:
		MouseButtonCondition(IEventQueue* events, IPlatformScreen::ButtonInfo*);
		MouseButtonCondition(IEventQueue* events, ButtonID, KeyModifierMask mask);
		virtual ~MouseButtonCondition();

		ButtonID				getButton() const;
		KeyModifierMask			getMask() const;

		// Condition overrides
		virtual Condition*		clone() const;
		virtual String			format() const;
		virtual EFilterStatus	match(const Event&);

	private:
		ButtonID				m_button;
		KeyModifierMask			m_mask;
		IEventQueue*			m_events;
	};

	// ScreenConnectedCondition
	class ScreenConnectedCondition : public Condition {
	public:
		ScreenConnectedCondition(IEventQueue* events, const String& screen);
		virtual ~ScreenConnectedCondition();

		// Condition overrides
		virtual Condition*		clone() const;
		virtual String			format() const;
		virtual EFilterStatus	match(const Event&);

	private:
		String					m_screen;
		IEventQueue*			m_events;
	};

	// -------------------------------------------------------------------------
	// Input Filter Action Classes
	// -------------------------------------------------------------------------
	
	class Action {
    public:
		Action();
		virtual	~Action();

		virtual Action*		clone() const = 0;
		virtual String			format() const = 0;

        virtual void			perform(const Event&) = 0;
    };
	
	// LockCursorToScreenAction
	class LockCursorToScreenAction : public Action {
	public:
		enum Mode { kOff, kOn, kToggle };

		LockCursorToScreenAction(IEventQueue* events, Mode = kToggle);

		Mode					getMode() const;

		// Action overrides
		virtual Action*		clone() const;
		virtual String			format() const;
		virtual void			perform(const Event&);

	private:
		Mode					m_mode;
		IEventQueue*			m_events;
	};
	
	// SwitchToScreenAction
	class SwitchToScreenAction : public Action {
	public:
		SwitchToScreenAction(IEventQueue* events, const String& screen);

		String					getScreen() const;

		// Action overrides
		virtual Action*		clone() const;
		virtual String			format() const;
		virtual void			perform(const Event&);

	private:
		String					m_screen;
		IEventQueue*			m_events;
	};
	
	// SwitchInDirectionAction
	class SwitchInDirectionAction : public Action {
	public:
		SwitchInDirectionAction(IEventQueue* events, EDirection);

		EDirection				getDirection() const;

		// Action overrides
		virtual Action*		clone() const;
		virtual String			format() const;
		virtual void			perform(const Event&);

	private:
		EDirection				m_direction;
		IEventQueue*			m_events;
	};
	
	// KeyboardBroadcastAction
	class KeyboardBroadcastAction : public Action {
	public:
		enum Mode { kOff, kOn, kToggle };

		KeyboardBroadcastAction(IEventQueue* events, Mode = kToggle);
		KeyboardBroadcastAction(IEventQueue* events, Mode, const std::set<String>& screens);

		Mode					getMode() const;
		std::set<String>		getScreens() const;

		// Action overrides
		virtual Action*		clone() const;
		virtual String			format() const;
		virtual void			perform(const Event&);

	private:
		Mode					m_mode;
		String					m_screens;
		IEventQueue*			m_events;
	};

	// KeystrokeAction
	class KeystrokeAction : public Action {
	public:
		KeystrokeAction(IEventQueue* events, IPlatformScreen::KeyInfo* adoptedInfo, bool press);
		~KeystrokeAction();

		void					adoptInfo(IPlatformScreen::KeyInfo*);
		const IPlatformScreen::KeyInfo*
								getInfo() const;
		bool					isOnPress() const;

		// Action overrides
		virtual Action*		clone() const;
		virtual String			format() const;
		virtual void			perform(const Event&);

	protected:
		virtual const char*		formatName() const;

	private:
		IPlatformScreen::KeyInfo*	m_keyInfo;
		bool					m_press;
		IEventQueue*			m_events;
	};

	// MouseButtonAction -- modifier combinations not implemented yet
	class MouseButtonAction : public Action {
	public:
		MouseButtonAction(IEventQueue* events,
									IPlatformScreen::ButtonInfo* adoptedInfo,
									bool press);
		~MouseButtonAction();

		const IPlatformScreen::ButtonInfo*
								getInfo() const;
		bool					isOnPress() const;

		// Action overrides
		virtual Action*		clone() const;
		virtual String			format() const;
		virtual void			perform(const Event&);

	protected:
		virtual const char*		formatName() const;

	private:
		IPlatformScreen::ButtonInfo*	m_buttonInfo;
		bool					m_press;
		IEventQueue*			m_events;
	};

	class Rule {
	public:
		Rule();
		Rule(Condition* adopted);
		Rule(const Rule&);
		~Rule();

		Rule& operator=(const Rule&);

		// replace the condition
		void			setCondition(Condition* adopted);

		// add an action to the rule
		void			adoptAction(Action*, bool onActivation);

		// remove an action from the rule
		void			removeAction(bool onActivation, UInt32 index);

		// replace an action in the rule
		void			replaceAction(Action* adopted,
							bool onActivation, UInt32 index);

		// enable/disable
		void			enable(PrimaryClient*);
		void			disable(PrimaryClient*);

		// event handling
		bool			handleEvent(const Event&);

		// convert rule to a string
		String			format() const;

		// get the rule's condition
		const Condition*
						getCondition() const;

		// get number of actions
		UInt32			getNumActions(bool onActivation) const;

		// get action by index
		const Action&	getAction(bool onActivation, UInt32 index) const;

	private:
		void			clear();
		void			copy(const Rule&);

	private:
		typedef std::vector<Action*> ActionList;

		Condition*		m_condition;
		ActionList		m_activateActions;
		ActionList		m_deactivateActions;
	};

	// -------------------------------------------------------------------------
	// Input Filter Class
	// -------------------------------------------------------------------------
	typedef std::vector<Rule> RuleList;

	InputFilter(IEventQueue* events);
	InputFilter(const InputFilter&);
	virtual ~InputFilter();

#ifdef TEST_ENV
	InputFilter() : m_primaryClient(NULL) { }
#endif

	InputFilter&		operator=(const InputFilter&);

	// add rule, adopting the condition and the actions
	void				addFilterRule(const Rule& rule);

	// remove a rule
	void				removeFilterRule(UInt32 index);

	// get rule by index
	Rule&				getRule(UInt32 index);

	// enable event filtering using the given primary client.  disable
	// if client is NULL.
	virtual void		setPrimaryClient(PrimaryClient* client);

	// convert rules to a string
	String				format(const String& linePrefix) const;

	// get number of rules
	UInt32				getNumRules() const;

	//! Compare filters
	bool				operator==(const InputFilter&) const;
	//! Compare filters
	bool				operator!=(const InputFilter&) const;

private:
	// event handling
	void				handleEvent(const Event&, void*);

private:
	RuleList			m_ruleList;
	PrimaryClient*		m_primaryClient;
	IEventQueue*		m_events;
};
