/** 
 * @file llcontrol.h
 * @brief A mechanism for storing "control state" for a program
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLCONTROL_H
#define LL_LLCONTROL_H

#include "llboost.h"
#include "llevent.h"
#include "llstring.h"
#include "llrect.h"
#include "llrefcount.h"
#include "llinstancetracker.h"

#include <vector>

// *NOTE: boost::visit_each<> generates warning 4675 on .net 2003
// Disable the warning for the boost includes.
#if LL_WINDOWS
# if (_MSC_VER >= 1300 && _MSC_VER < 1400)
#   pragma warning(push)
#   pragma warning( disable : 4675 )
# endif
#endif

#include <boost/bind.hpp>

#if LL_WINDOWS
	#pragma warning (push)
	#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
	#pragma warning (disable : 4264) 
#endif
#include <boost/signals2.hpp>
#if LL_WINDOWS
	#pragma warning (pop)
#endif

#if LL_WINDOWS
# if (_MSC_VER >= 1300 && _MSC_VER < 1400)
#   pragma warning(pop)
# endif
#endif

class LLVector3;
class LLVector3d;
class LLQuaternion;
class LLColor4;
class LLColor3;

// if this is changed, also modify mTypeString in llcontrol.h
typedef enum e_control_type
{
	TYPE_U32 = 0,
	TYPE_S32,
	TYPE_F32,
	TYPE_BOOLEAN,
	TYPE_STRING,
	TYPE_VEC3,
	TYPE_VEC3D,
	TYPE_QUAT,
	TYPE_RECT,
	TYPE_COL4,
	TYPE_COL3,
	TYPE_LLSD,
	TYPE_COUNT
} eControlType;

typedef enum e_sanity_type
{
	SANITY_TYPE_NONE = 0,
	SANITY_TYPE_EQUALS,
	SANITY_TYPE_NOT_EQUALS,
	SANITY_TYPE_LESS_THAN,
	SANITY_TYPE_GREATER_THAN,
	SANITY_TYPE_LESS_THAN_EQUALS,
	SANITY_TYPE_GREATER_THAN_EQUALS,
	SANITY_TYPE_BETWEEN,
	SANITY_TYPE_NOT_BETWEEN,
	SANITY_TYPE_COUNT
} eSanityType;

class LLControlVariable : public LLRefCount
{
	LOG_CLASS(LLControlVariable);

	friend class LLControlGroup;
	
public:
	typedef boost::signals2::signal<bool(LLControlVariable* control, const LLSD&), boost_boolean_combiner> validate_signal_t;
	typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&, const LLSD&)> commit_signal_t;
	typedef boost::signals2::signal<void(LLControlVariable* control, const LLSD&)> sanity_signal_t;

	enum ePersist
	{
		PERSIST_NO,                 // don't save this var
		PERSIST_NONDFT,             // save this var if differs from default
		PERSIST_ALWAYS              // save this var even if has default value
	};

private:
	std::string		mName;
	std::string		mComment;
	eControlType		mType;
	eSanityType		mSanityType;
	std::string		mSanityComment;
	ePersist		mPersist;
	bool			mCanBackup;		// <FS:Zi> Backup Settings
	bool			mHideFromSettingsEditor;
	std::vector<LLSD> mValues;
	std::vector<LLSD> mSanityValues;

	commit_signal_t mCommitSignal;
	validate_signal_t mValidateSignal;
	sanity_signal_t mSanitySignal;
	
public:
	LLControlVariable(const std::string& name, eControlType type,
		LLSD initial, const std::string& comment,
		eSanityType sanityType,
		LLSD sanityValues,
		const std::string& sanityComment,
		// <FS:Zi> Backup Settings
		// ePersist persist = PERSIST_NONDFT, bool hidefromsettingseditor = false
		ePersist persist = PERSIST_NONDFT, bool can_backup = true, bool hidefromsettingseditor = false
		// </FS:Zi>
	);

	virtual ~LLControlVariable();
	
	const std::string& getName() const { return mName; }
	const std::string& getComment() const { return mComment; }
	eSanityType getSanityType() { return mSanityType; }
	const std::string& getSanityComment() const { return mSanityComment; }
	const std::vector<LLSD>& getSanityValues() { return mSanityValues; };

	eControlType type()		{ return mType; }
	bool isType(eControlType tp) { return tp == mType; }

	void resetToDefault(bool fire_signal = false);

	commit_signal_t* getSignal() { return &mCommitSignal; } // shorthand for commit signal
	commit_signal_t* getCommitSignal() { return &mCommitSignal; }
	validate_signal_t* getValidateSignal() { return &mValidateSignal; }
	sanity_signal_t* getSanitySignal() { return &mSanitySignal; }

// [RLVa:KB] - Patch: RLVa-2.1.0
	bool hasUnsavedValue() { return mValues.size() > 2; }
// [/RLVa:KB]
	bool isDefault() { return (mValues.size() == 1); }
	bool isSane();
	bool shouldSave(bool nondefault_only);
	bool isPersisted() { return mPersist != PERSIST_NO; }
	bool isBackupable() { return mCanBackup; }		// <FS:Zi> Backup Settings
	bool isHiddenFromSettingsEditor() { return mHideFromSettingsEditor; }
	LLSD get()			const	{ return getValue(); }
	LLSD getValue()		const	{ return mValues.back(); }
	LLSD getDefault()	const	{ return mValues.front(); }
	LLSD getSaveValue() const;

	void set(const LLSD& val)	{ setValue(val); }
	void setValue(const LLSD& value, bool saved_value = TRUE);
	void setDefaultValue(const LLSD& value);
	void setPersist(ePersist);
	void setBackupable(bool state);		// <FS:Zi> Backup Settings
	void setHiddenFromSettingsEditor(bool hide);
	void setComment(const std::string& comment);

private:
	void firePropertyChanged(const LLSD &pPreviousValue)
	{
		mCommitSignal(this, mValues.back(), pPreviousValue);
	}
	LLSD getComparableValue(const LLSD& value);
	bool llsd_compare(const LLSD& a, const LLSD & b);
};

typedef LLPointer<LLControlVariable> LLControlVariablePtr;

//! Helper functions for converting between static types and LLControl values
template <class T> 
eControlType get_control_type()
{
	LL_WARNS() << "Usupported control type: " << typeid(T).name() << "." << LL_ENDL;
	return TYPE_COUNT;
}

template <class T> 
LLSD convert_to_llsd(const T& in)
{
	// default implementation
	return LLSD(in);
}

template <class T>
T convert_from_llsd(const LLSD& sd, eControlType type, const std::string& control_name)
{
	// needs specialization
	return T(sd);
}

//const U32 STRING_CACHE_SIZE = 10000;
class LLControlGroup : public LLInstanceTracker<LLControlGroup, std::string>
{
	LOG_CLASS(LLControlGroup);

protected:
	typedef std::map<std::string, LLControlVariablePtr > ctrl_name_table_t;
	ctrl_name_table_t mNameTable;
	static const std::string mTypeString[TYPE_COUNT];
	static const std::string mSanityTypeString[SANITY_TYPE_COUNT];

public:
	static eControlType typeStringToEnum(const std::string& typestr);
	static eSanityType sanityTypeStringToEnum(const std::string& sanitystr);
	static std::string typeEnumToString(eControlType typeenum);	
	static std::string sanityTypeEnumToString(eSanityType sanitytypeenum);	

	LLControlGroup(const std::string& name);
	~LLControlGroup();
	void cleanup();

	LLControlVariablePtr getControl(const std::string& name);

	struct ApplyFunctor
	{
		virtual ~ApplyFunctor() {};
		virtual void apply(const std::string& name, LLControlVariable* control) = 0;
	};
	void applyToAll(ApplyFunctor* func);

	// <FS:Zi> Backup Settings
	//LLControlVariable* declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, LLControlVariable::ePersist persist, BOOL hidefromsettingseditor = FALSE);
	LLControlVariable* declareControl(const std::string& name, eControlType type, const LLSD initial_val, const std::string& comment, eSanityType sanity_type, LLSD sanity_value, const std::string& sanity_comment, LLControlVariable::ePersist persist, BOOL can_backup = TRUE, BOOL hidefromsettingseditor = FALSE);
	// </FS:Zi>

	LLControlVariable* declareU32(const std::string& name, U32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareS32(const std::string& name, S32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareF32(const std::string& name, F32 initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareBOOL(const std::string& name, BOOL initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareString(const std::string& name, const std::string &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareVec3(const std::string& name, const LLVector3 &initial_val,const std::string& comment,  LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareVec3d(const std::string& name, const LLVector3d &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareQuat(const std::string& name, const LLQuaternion &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareRect(const std::string& name, const LLRect &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareColor4(const std::string& name, const LLColor4 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareColor3(const std::string& name, const LLColor3 &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);
	LLControlVariable* declareLLSD(const std::string& name, const LLSD &initial_val, const std::string& comment, LLControlVariable::ePersist persist = LLControlVariable::PERSIST_NONDFT);

	std::string getString(const std::string& name);
	std::string getText(const std::string& name);
	BOOL		getBOOL(const std::string& name);
	S32			getS32(const std::string& name);
	F32			getF32(const std::string& name);
	U32			getU32(const std::string& name);
	
	LLWString	getWString(const std::string& name);
	LLVector3	getVector3(const std::string& name);
	LLVector3d	getVector3d(const std::string& name);	
	LLRect		getRect(const std::string& name);
	LLSD        getLLSD(const std::string& name);
	LLQuaternion	getQuaternion(const std::string& name);

	LLColor4	getColor(const std::string& name);
	LLColor4	getColor4(const std::string& name);
	LLColor3	getColor3(const std::string& name);

	LLSD		asLLSD(bool diffs_only);
	
	// generic getter
	template<typename T> T get(const std::string& name)
	{
        LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD;
		LLControlVariable* control = getControl(name);
		LLSD value;
		eControlType type = TYPE_COUNT;

		if (control)		
		{
			value = control->get();
			type = control->type();
		}
		else
		{
			LL_WARNS() << "Control " << name << " not found." << LL_ENDL;
			return T();
		}
		return convert_from_llsd<T>(value, type, name);
	}

	void	setBOOL(const std::string& name, BOOL val);
	void	setS32(const std::string& name, S32 val);
	void	setF32(const std::string& name, F32 val);
	void	setU32(const std::string& name, U32 val);
	void	setString(const std::string&  name, const std::string& val);
	void	setVector3(const std::string& name, const LLVector3 &val);
	void	setVector3d(const std::string& name, const LLVector3d &val);
	void	setQuaternion(const std::string& name, const LLQuaternion &val);
	void	setRect(const std::string& name, const LLRect &val);
	void	setColor4(const std::string& name, const LLColor4 &val);
	void    setLLSD(const std::string& name, const LLSD& val);

	// type agnostic setter that takes LLSD
	void	setUntypedValue(const std::string& name, const LLSD& val);

	// generic setter
	template<typename T> void set(const std::string& name, const T& val)
	{
		LLControlVariable* control = getControl(name);
	
		if (control && control->isType(get_control_type<T>()))
		{
			control->set(convert_to_llsd(val));
		}
		else
		{
			LL_WARNS() << "Invalid control " << name << LL_ENDL;
		}
	}
	
	BOOL    controlExists(const std::string& name);

	// Returns number of controls loaded, 0 if failed
	// If require_declaration is false, will auto-declare controls it finds
	// as the given type.
	U32	loadFromFileLegacy(const std::string& filename, BOOL require_declaration = TRUE, eControlType declare_as = TYPE_STRING);
 	U32 saveToFile(const std::string& filename, BOOL nondefault_only);
 	U32	loadFromFile(const std::string& filename, bool default_values = false, bool save_values = true);
	void	resetToDefaults();
	void	incrCount(const std::string& name);

	bool	mSettingsProfile;
};


//! Publish/Subscribe object to interact with LLControlGroups.

//! Use an LLCachedControl instance to connect to a LLControlVariable
//! without have to manually create and bind a listener to a local
//! object.
template <class T>
class LLControlCache : public LLRefCount, public LLInstanceTracker<LLControlCache<T>, std::string>
{
public:
	// This constructor will declare a control if it doesn't exist in the contol group
	LLControlCache(LLControlGroup& group,
					const std::string& name, 
					const T& default_value, 
					const std::string& comment)
	:	LLInstanceTracker<LLControlCache<T>, std::string >(name)
	{
		if(!group.controlExists(name))
		{
			if(!declareTypedControl(group, name, default_value, comment))
			{
				LL_ERRS() << "The control could not be created!!!" << LL_ENDL;
			}
		}

		bindToControl(group, name);
	}

	LLControlCache(LLControlGroup& group,
					const std::string& name)
	:	LLInstanceTracker<LLControlCache<T>, std::string >(name)
	{
		if(!group.controlExists(name))
		{
			LL_ERRS() << "Control named \"" << name << "\" not found." << LL_ENDL;
		}

		bindToControl(group, name);
	}

	~LLControlCache()
	{
	}

	const T& getValue() const { return mCachedValue; }
	
private:
	void bindToControl(LLControlGroup& group, const std::string& name)
	{
		LLControlVariablePtr controlp = group.getControl(name);
		mType = controlp->type();
		mCachedValue = convert_from_llsd<T>(controlp->get(), mType, name);

		// Add a listener to the controls signal...
		// NOTE: All listeners connected to 0 group, for guaranty that variable handlers (gSavedSettings) call last
		mConnection = controlp->getSignal()->connect(0,
			boost::bind(&LLControlCache<T>::handleValueChange, this, _2)
			);
		mType = controlp->type();
	}
	bool declareTypedControl(LLControlGroup& group,
							const std::string& name, 
							 const T& default_value,
							 const std::string& comment)
	{
		LLSD init_value;
		eControlType type = get_control_type<T>();
		init_value = convert_to_llsd(default_value);
		if(type < TYPE_COUNT)
		{
			// <FS:Zi> Backup Settings
			// group.declareControl(name, type, init_value, comment, SANITY_TYPE_NONE, LLSD(), std::string(""), LLControlVariable::PERSIST_NO);
			group.declareControl(name, type, init_value, comment, SANITY_TYPE_NONE, LLSD(), std::string(""), LLControlVariable::PERSIST_NO);
			// </FS_Zi>
			return true;
		}
		return false;
	}

	bool handleValueChange(const LLSD& newvalue)
	{
		mCachedValue = convert_from_llsd<T>(newvalue, mType, "");
		return true;
	}

private:
    T							mCachedValue;
	eControlType				mType;
    boost::signals2::scoped_connection	mConnection;
};

template <typename T>
class LLCachedControl
{
public:
	LLCachedControl(LLControlGroup& group,
					const std::string& name,
					const T& default_value, 
					const std::string& comment = "Declared In Code")
	{
		mCachedControlPtr = LLControlCache<T>::getInstance(name).get();
		if (! mCachedControlPtr)
		{
			mCachedControlPtr = new LLControlCache<T>(group, name, default_value, comment);
		}
	}

	LLCachedControl(LLControlGroup& group,
					const std::string& name)
	{
		mCachedControlPtr = LLControlCache<T>::getInstance(name).get();
		if (! mCachedControlPtr)
		{
			mCachedControlPtr = new LLControlCache<T>(group, name);
		}
	}

	operator const T&() const { return mCachedControlPtr->getValue(); }
	operator boost::function<const T&()> () const { return boost::function<const T&()>(*this); }
	const T& operator()() { return mCachedControlPtr->getValue(); }

private:
	LLPointer<LLControlCache<T> > mCachedControlPtr;
};

template <> eControlType get_control_type<U32>();
template <> eControlType get_control_type<S32>();
template <> eControlType get_control_type<F32>();
template <> eControlType get_control_type<bool>(); 
// Yay BOOL, its really an S32.
//template <> eControlType get_control_type<BOOL> () 
template <> eControlType get_control_type<std::string>();
template <> eControlType get_control_type<LLVector3>();
template <> eControlType get_control_type<LLVector3d>();
template <> eControlType get_control_type<LLQuaternion>();
template <> eControlType get_control_type<LLRect>();
template <> eControlType get_control_type<LLColor4>();
template <> eControlType get_control_type<LLColor3>();
template <> eControlType get_control_type<LLSD>();

template <> LLSD convert_to_llsd<U32>(const U32& in);
template <> LLSD convert_to_llsd<LLVector3>(const LLVector3& in);
template <> LLSD convert_to_llsd<LLVector3d>(const LLVector3d& in);
template <> LLSD convert_to_llsd<LLQuaternion>(const LLQuaternion& in);
template <> LLSD convert_to_llsd<LLRect>(const LLRect& in);
template <> LLSD convert_to_llsd<LLColor4>(const LLColor4& in);
template <> LLSD convert_to_llsd<LLColor3>(const LLColor3& in);

template<> std::string convert_from_llsd<std::string>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLWString convert_from_llsd<LLWString>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLVector3 convert_from_llsd<LLVector3>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLVector3d convert_from_llsd<LLVector3d>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLQuaternion convert_from_llsd<LLQuaternion>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLRect convert_from_llsd<LLRect>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> bool convert_from_llsd<bool>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> S32 convert_from_llsd<S32>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> F32 convert_from_llsd<F32>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> U32 convert_from_llsd<U32>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLColor3 convert_from_llsd<LLColor3>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLColor4 convert_from_llsd<LLColor4>(const LLSD& sd, eControlType type, const std::string& control_name);
template<> LLSD convert_from_llsd<LLSD>(const LLSD& sd, eControlType type, const std::string& control_name);

//#define TEST_CACHED_CONTROL 1
#ifdef TEST_CACHED_CONTROL
void test_cached_control();
#endif // TEST_CACHED_CONTROL

#endif
