#include "base/exception.hpp"
#include "base/objectlock.hpp"
#include "base/utility.hpp"
#include "base/convert.hpp"
#include "base/debug.hpp"
#include "base/dependencygraph.hpp"
#include "base/logger.hpp"
#include "base/function.hpp"
#include "base/configobject.hpp"
#include "base/configtype.hpp"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
namespace icinga
{

TypeImpl<CustomVarObject>::TypeImpl()
{ }

TypeImpl<CustomVarObject>::~TypeImpl()
{ }

String TypeImpl<CustomVarObject>::GetName() const
{
	return "CustomVarObject";
}

int TypeImpl<CustomVarObject>::GetAttributes() const
{
	return 1;
}

Type::Ptr TypeImpl<CustomVarObject>::GetBaseType() const
{
	return ConfigObject::TypeInstance;
}

int TypeImpl<CustomVarObject>::GetFieldId(const String& name) const
{
	int offset = ConfigObject::TypeInstance->GetFieldCount();

	switch (static_cast<int>(Utility::SDBM(name, 1))) {
		case 118:
			if (name == "vars")
				return offset + 0;

			break;
	}

	return ConfigObject::TypeInstance->GetFieldId(name);
}

Field TypeImpl<CustomVarObject>::GetFieldInfo(int id) const
{
	int real_id = id - ConfigObject::TypeInstance->GetFieldCount();
	if (real_id < 0) { return ConfigObject::TypeInstance->GetFieldInfo(id); }
	switch (real_id) {
		case 0:
			return {0, "Dictionary", "vars", "vars", nullptr, 65538, 0};
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

int TypeImpl<CustomVarObject>::GetFieldCount() const
{
	return 1 + ConfigObject::TypeInstance->GetFieldCount();
}

ObjectFactory TypeImpl<CustomVarObject>::GetFactory() const
{
	return TypeHelper<CustomVarObject, false>::GetFactory();
}

int TypeImpl<CustomVarObject>::GetActivationPriority() const
{
	return 0;
}

void TypeImpl<CustomVarObject>::RegisterAttributeHandler(int fieldId, const Type::AttributeHandler& callback)
{
	int real_id = fieldId - ConfigObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { ConfigObject::TypeInstance->RegisterAttributeHandler(fieldId, callback); return; }
	switch (real_id) {
		case 0:
			ObjectImpl<CustomVarObject>::OnVarsChanged.connect(callback);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<CustomVarObject>::Validate(int types, const ValidationUtils& utils)
{
	ConfigObject::Validate(types, utils);

	if (2 & types)
		ValidateVars(Lazy<Dictionary::Ptr>([this]() { return GetVars(); }), utils);
}

void ObjectImpl<CustomVarObject>::SimpleValidateVars(const Lazy<Dictionary::Ptr>& value, const ValidationUtils& utils)
{
}

ObjectImpl<CustomVarObject>::ObjectImpl()
{
	SetVars(GetDefaultVars(), true);
}

ObjectImpl<CustomVarObject>::~ObjectImpl()
{ }

void ObjectImpl<CustomVarObject>::SetField(int id, const Value& value, bool suppress_events, const Value& cookie)
{
	int real_id = id - ConfigObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { ConfigObject::SetField(id, value, suppress_events, cookie); return; }
	switch (real_id) {
		case 0:
			SetVars(value, suppress_events, cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

Value ObjectImpl<CustomVarObject>::GetField(int id) const
{
	int real_id = id - ConfigObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { return ConfigObject::GetField(id); }
	switch (real_id) {
		case 0:
			return GetVars();
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<CustomVarObject>::ValidateField(int id, const Lazy<Value>& lvalue, const ValidationUtils& utils)
{
	int real_id = id - ConfigObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { ConfigObject::ValidateField(id, lvalue, utils); return; }
	switch (real_id) {
		case 0:
			ValidateVars(lvalue, utils);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<CustomVarObject>::NotifyField(int id, const Value& cookie)
{
	int real_id = id - ConfigObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { ConfigObject::NotifyField(id, cookie); return; }
	switch (real_id) {
		case 0:
			NotifyVars(cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

Object::Ptr ObjectImpl<CustomVarObject>::NavigateField(int id) const
{
	int real_id = id - ConfigObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { return ConfigObject::NavigateField(id); }
	throw std::runtime_error("Invalid field ID.");
}

Dictionary::Ptr ObjectImpl<CustomVarObject>::GetVars() const
{
	return m_Vars.load();
}

void ObjectImpl<CustomVarObject>::SetVars(const Dictionary::Ptr& value, bool suppress_events, const Value& cookie)
{
	Value oldValue = GetVars();
	auto *dobj = dynamic_cast<ConfigObject *>(this);
	m_Vars.store(value);
	if (!suppress_events) {
		NotifyVars(cookie);
		if (!dobj || dobj->IsActive())
			OnVarsChangedWithOldValue(static_cast<CustomVarObject *>(this), oldValue, value);
	}

}

void ObjectImpl<CustomVarObject>::NotifyVars(const Value& cookie)
{
	auto *dobj = dynamic_cast<ConfigObject *>(this);
	if (!dobj || dobj->IsActive())
		OnVarsChanged(static_cast<CustomVarObject *>(this), cookie);
}

Dictionary::Ptr ObjectImpl<CustomVarObject>::GetDefaultVars() const
{
	return Dictionary::Ptr();
}


boost::signals2::signal<void (const intrusive_ptr<CustomVarObject>&, const Value&)> ObjectImpl<CustomVarObject>::OnVarsChanged;


boost::signals2::signal<void (const intrusive_ptr<CustomVarObject>&, const Value&, const Value&)> ObjectImpl<CustomVarObject>::OnVarsChangedWithOldValue;

void ObjectImpl<CustomVarObject>::ValidateVars(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateVars(lvalue, utils);
}

}
#pragma GCC diagnostic pop
