#ifndef COMMAND_TI
#define COMMAND_TI

#include "base/object.hpp"
#include "base/type.hpp"
#include "base/value.hpp"
#include "base/array.hpp"
#include "base/atomic.hpp"
#include "base/dictionary.hpp"
#include <boost/signals2.hpp>

#include "icinga/customvarobject.hpp"

#include "base/function.hpp"

namespace icinga
{

class Command;

template<>
struct TypeHelper<Command, false>
{
	static ObjectFactory GetFactory()
	{
		return nullptr;
	}
};

template<>
class TypeImpl<Command> : public TypeImpl<CustomVarObject>
{
public:
	DECLARE_PTR_TYPEDEFS(TypeImpl<Command>);

	TypeImpl();
	~TypeImpl() override;

	String GetName() const override;
	int GetAttributes() const override;
	Type::Ptr GetBaseType() const override;
	int GetFieldId(const String& name) const override;
	Field GetFieldInfo(int id) const override;
	int GetFieldCount() const override;
	ObjectFactory GetFactory() const override;
	int GetActivationPriority() const override;
public:
	void RegisterAttributeHandler(int fieldId, const Type::AttributeHandler& callback) override;
};


template<>
class ObjectImpl<Command> : public CustomVarObject
{
public:
	DECLARE_PTR_TYPEDEFS(ObjectImpl<Command>);

	void Validate(int types, const ValidationUtils& utils) override;
	void SimpleValidateCommandLine(const Lazy<Value>& value, const ValidationUtils& utils);
	void SimpleValidateArguments(const Lazy<Value>& value, const ValidationUtils& utils);
	void SimpleValidateEnv(const Lazy<Dictionary::Ptr>& value, const ValidationUtils& utils);
	void SimpleValidateExecute(const Lazy<Function::Ptr>& value, const ValidationUtils& utils);
	void SimpleValidateTimeout(const Lazy<int>& value, const ValidationUtils& utils);
public:
	ObjectImpl<Command>();
public:
	~ObjectImpl<Command>() override;
public:
	void SetField(int id, const Value& value, bool suppress_events = false, const Value& cookie = Empty) override;
public:
	Value GetField(int id) const override;
public:
	void ValidateField(int id, const Lazy<Value>& lvalue, const ValidationUtils& utils) override;
public:
	void NotifyField(int id, const Value& cookie = Empty) override;
public:
	Object::Ptr NavigateField(int id) const override;
public:
	Value GetCommandLine() const;
public:
	Value GetArguments() const;
public:
	Dictionary::Ptr GetEnv() const;
public:
	Function::Ptr GetExecute() const;
public:
	int GetTimeout() const;
public:
	void SetCommandLine(const Value& value, bool suppress_events = false, const Value& cookie = Empty);
public:
	void SetArguments(const Value& value, bool suppress_events = false, const Value& cookie = Empty);
public:
	void SetEnv(const Dictionary::Ptr& value, bool suppress_events = false, const Value& cookie = Empty);
public:
	void SetExecute(const Function::Ptr& value, bool suppress_events = false, const Value& cookie = Empty);
public:
	void SetTimeout(int value, bool suppress_events = false, const Value& cookie = Empty);
protected:
public:
	virtual void NotifyCommandLine(const Value& cookie = Empty);
public:
	virtual void NotifyArguments(const Value& cookie = Empty);
public:
	virtual void NotifyEnv(const Value& cookie = Empty);
public:
	virtual void NotifyExecute(const Value& cookie = Empty);
public:
	virtual void NotifyTimeout(const Value& cookie = Empty);
private:
	inline Value GetDefaultCommandLine() const;
private:
	inline Value GetDefaultArguments() const;
private:
	inline Dictionary::Ptr GetDefaultEnv() const;
private:
	inline Function::Ptr GetDefaultExecute() const;
private:
	inline int GetDefaultTimeout() const;
protected:
	virtual void ValidateCommandLine(const Lazy<Value>& lvalue, const ValidationUtils& utils);
protected:
	virtual void ValidateArguments(const Lazy<Value>& lvalue, const ValidationUtils& utils);
protected:
	virtual void ValidateEnv(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils);
protected:
	virtual void ValidateExecute(const Lazy<Function::Ptr>& lvalue, const ValidationUtils& utils);
protected:
	virtual void ValidateTimeout(const Lazy<int>& lvalue, const ValidationUtils& utils);
private:
	AtomicOrLocked<Value> m_CommandLine;
	AtomicOrLocked<Value> m_Arguments;
	AtomicOrLocked<Dictionary::Ptr> m_Env;
	AtomicOrLocked<Function::Ptr> m_Execute;
	AtomicOrLocked<int> m_Timeout;
public:
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&)> OnCommandLineChanged;
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&)> OnArgumentsChanged;
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&, const Value&)> OnArgumentsChangedWithOldValue;
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&)> OnEnvChanged;
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&, const Value&)> OnEnvChangedWithOldValue;
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&)> OnExecuteChanged;
	static boost::signals2::signal<void (const intrusive_ptr<Command>&, const Value&)> OnTimeoutChanged;
};

}
#endif /* COMMAND_TI */
