#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<ServiceGroup>::TypeImpl()
{ }

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

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

int TypeImpl<ServiceGroup>::GetAttributes() const
{
	return 0;
}

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

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

	switch (static_cast<int>(Utility::SDBM(name, 1))) {
		case 97:
			if (name == "action_url")
				return offset + 3;

			break;
		case 100:
			if (name == "display_name")
				return offset + 0;

			break;
		case 103:
			if (name == "groups")
				return offset + 4;

			break;
		case 110:
			if (name == "notes")
				return offset + 1;
			if (name == "notes_url")
				return offset + 2;

			break;
	}

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

Field TypeImpl<ServiceGroup>::GetFieldInfo(int id) const
{
	int real_id = id - CustomVarObject::TypeInstance->GetFieldCount();
	if (real_id < 0) { return CustomVarObject::TypeInstance->GetFieldInfo(id); }
	switch (real_id) {
		case 0:
			return {0, "String", "display_name", "display_name", nullptr, 2, 0};
		case 1:
			return {1, "String", "notes", "notes", nullptr, 2, 0};
		case 2:
			return {2, "String", "notes_url", "notes_url", nullptr, 2, 0};
		case 3:
			return {3, "String", "action_url", "action_url", nullptr, 2, 0};
		case 4:
			return {4, "Array", "groups", "groups", "ServiceGroup", 1026, 1};
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

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

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

void TypeImpl<ServiceGroup>::RegisterAttributeHandler(int fieldId, const Type::AttributeHandler& callback)
{
	int real_id = fieldId - CustomVarObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { CustomVarObject::TypeInstance->RegisterAttributeHandler(fieldId, callback); return; }
	switch (real_id) {
		case 0:
			ObjectImpl<ServiceGroup>::OnDisplayNameChanged.connect(callback);
			break;
		case 1:
			ObjectImpl<ServiceGroup>::OnNotesChanged.connect(callback);
			break;
		case 2:
			ObjectImpl<ServiceGroup>::OnNotesUrlChanged.connect(callback);
			break;
		case 3:
			ObjectImpl<ServiceGroup>::OnActionUrlChanged.connect(callback);
			break;
		case 4:
			ObjectImpl<ServiceGroup>::OnGroupsChanged.connect(callback);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

	if (2 & types)
		ValidateDisplayName(Lazy<String>([this]() { return GetDisplayName(); }), utils);
	if (2 & types)
		ValidateNotes(Lazy<String>([this]() { return GetNotes(); }), utils);
	if (2 & types)
		ValidateNotesUrl(Lazy<String>([this]() { return GetNotesUrl(); }), utils);
	if (2 & types)
		ValidateActionUrl(Lazy<String>([this]() { return GetActionUrl(); }), utils);
	if (2 & types)
		ValidateGroups(Lazy<Array::Ptr>([this]() { return GetGroups(); }), utils);
}

void ObjectImpl<ServiceGroup>::SimpleValidateDisplayName(const Lazy<String>& value, const ValidationUtils& utils)
{
}

void ObjectImpl<ServiceGroup>::SimpleValidateNotes(const Lazy<String>& value, const ValidationUtils& utils)
{
}

void ObjectImpl<ServiceGroup>::SimpleValidateNotesUrl(const Lazy<String>& value, const ValidationUtils& utils)
{
}

void ObjectImpl<ServiceGroup>::SimpleValidateActionUrl(const Lazy<String>& value, const ValidationUtils& utils)
{
}

void ObjectImpl<ServiceGroup>::SimpleValidateGroups(const Lazy<Array::Ptr>& avalue, const ValidationUtils& utils)
{
	if (avalue()) {
		ObjectLock olock(avalue());
		for (const Value& value : avalue()) {
			if (!value.IsEmpty() && !value.IsString())
				BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast<ConfigObject *>(this), { "groups" }, "It is not allowed to specify '" + value + "' of type '" + value.GetTypeName() + "' as 'ServiceGroup' name. Expected type of 'ServiceGroup' name: 'String'."));
			if (value.IsEmpty() || !utils.ValidateName("ServiceGroup", value))
				BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast<ConfigObject *>(this), { "groups" }, "Object '" + value + "' of type 'ServiceGroup' does not exist."));
		}
	}
}

ObjectImpl<ServiceGroup>::ObjectImpl()
{
	SetDisplayName(GetDefaultDisplayName(), true);
	SetNotes(GetDefaultNotes(), true);
	SetNotesUrl(GetDefaultNotesUrl(), true);
	SetActionUrl(GetDefaultActionUrl(), true);
	SetGroups(GetDefaultGroups(), true);
}

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

void ObjectImpl<ServiceGroup>::SetField(int id, const Value& value, bool suppress_events, const Value& cookie)
{
	int real_id = id - CustomVarObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { CustomVarObject::SetField(id, value, suppress_events, cookie); return; }
	switch (real_id) {
		case 0:
			SetDisplayName(value, suppress_events, cookie);
			break;
		case 1:
			SetNotes(value, suppress_events, cookie);
			break;
		case 2:
			SetNotesUrl(value, suppress_events, cookie);
			break;
		case 3:
			SetActionUrl(value, suppress_events, cookie);
			break;
		case 4:
			SetGroups(value, suppress_events, cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

Value ObjectImpl<ServiceGroup>::GetField(int id) const
{
	int real_id = id - CustomVarObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { return CustomVarObject::GetField(id); }
	switch (real_id) {
		case 0:
			return GetDisplayName();
		case 1:
			return GetNotes();
		case 2:
			return GetNotesUrl();
		case 3:
			return GetActionUrl();
		case 4:
			return GetGroups();
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<ServiceGroup>::ValidateField(int id, const Lazy<Value>& lvalue, const ValidationUtils& utils)
{
	int real_id = id - CustomVarObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { CustomVarObject::ValidateField(id, lvalue, utils); return; }
	switch (real_id) {
		case 0:
			ValidateDisplayName(lvalue, utils);
			break;
		case 1:
			ValidateNotes(lvalue, utils);
			break;
		case 2:
			ValidateNotesUrl(lvalue, utils);
			break;
		case 3:
			ValidateActionUrl(lvalue, utils);
			break;
		case 4:
			ValidateGroups(lvalue, utils);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<ServiceGroup>::NotifyField(int id, const Value& cookie)
{
	int real_id = id - CustomVarObject::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { CustomVarObject::NotifyField(id, cookie); return; }
	switch (real_id) {
		case 0:
			NotifyDisplayName(cookie);
			break;
		case 1:
			NotifyNotes(cookie);
			break;
		case 2:
			NotifyNotesUrl(cookie);
			break;
		case 3:
			NotifyActionUrl(cookie);
			break;
		case 4:
			NotifyGroups(cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

String ObjectImpl<ServiceGroup>::GetDisplayName() const
{

			String displayName = m_DisplayName.load();
			if (displayName.IsEmpty())
				return GetName();
			else
				return displayName;
		
}

String ObjectImpl<ServiceGroup>::GetNotes() const
{
	return m_Notes.load();
}

String ObjectImpl<ServiceGroup>::GetNotesUrl() const
{
	return m_NotesUrl.load();
}

String ObjectImpl<ServiceGroup>::GetActionUrl() const
{
	return m_ActionUrl.load();
}

Array::Ptr ObjectImpl<ServiceGroup>::GetGroups() const
{
	return m_Groups.load();
}

void ObjectImpl<ServiceGroup>::SetDisplayName(const String& value, bool suppress_events, const Value& cookie)
{
	m_DisplayName.store(value);
	if (!suppress_events) {
		NotifyDisplayName(cookie);
	}

}

void ObjectImpl<ServiceGroup>::SetNotes(const String& value, bool suppress_events, const Value& cookie)
{
	m_Notes.store(value);
	if (!suppress_events) {
		NotifyNotes(cookie);
	}

}

void ObjectImpl<ServiceGroup>::SetNotesUrl(const String& value, bool suppress_events, const Value& cookie)
{
	m_NotesUrl.store(value);
	if (!suppress_events) {
		NotifyNotesUrl(cookie);
	}

}

void ObjectImpl<ServiceGroup>::SetActionUrl(const String& value, bool suppress_events, const Value& cookie)
{
	m_ActionUrl.store(value);
	if (!suppress_events) {
		NotifyActionUrl(cookie);
	}

}

void ObjectImpl<ServiceGroup>::SetGroups(const Array::Ptr& value, bool suppress_events, const Value& cookie)
{
	Value oldValue = GetGroups();
	auto *dobj = dynamic_cast<ConfigObject *>(this);
	m_Groups.store(value);
	if (!dobj || dobj->IsActive())
		TrackGroups(oldValue, value);
	if (!suppress_events) {
		NotifyGroups(cookie);
	}

}

void ObjectImpl<ServiceGroup>::TrackGroups(const Array::Ptr& oldValue, const Array::Ptr& newValue)
{
	if (oldValue) {
		ObjectLock olock(oldValue);
		for (auto& ref : oldValue) {
			DependencyGraph::RemoveDependency(this, ConfigObject::GetObject<ServiceGroup>(ref).get());
		}
	}
	if (newValue) {
		ObjectLock olock(newValue);
		for (auto& ref : newValue) {
			DependencyGraph::AddDependency(this, ConfigObject::GetObject<ServiceGroup>(ref).get());
		}
	}
}

void ObjectImpl<ServiceGroup>::Start(bool runtimeCreated)
{
	CustomVarObject::Start(runtimeCreated);

	TrackGroups(Empty, GetGroups());
}

void ObjectImpl<ServiceGroup>::Stop(bool runtimeRemoved)
{
	CustomVarObject::Stop(runtimeRemoved);

	TrackGroups(GetGroups(), Empty);
}

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

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

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

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

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

String ObjectImpl<ServiceGroup>::GetDefaultDisplayName() const
{
	return String();
}

String ObjectImpl<ServiceGroup>::GetDefaultNotes() const
{
	return String();
}

String ObjectImpl<ServiceGroup>::GetDefaultNotesUrl() const
{
	return String();
}

String ObjectImpl<ServiceGroup>::GetDefaultActionUrl() const
{
	return String();
}

Array::Ptr ObjectImpl<ServiceGroup>::GetDefaultGroups() const
{
	return Array::Ptr();
}


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


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


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


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


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

void ObjectImpl<ServiceGroup>::ValidateActionUrl(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateActionUrl(lvalue, utils);
}

void ObjectImpl<ServiceGroup>::ValidateDisplayName(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateDisplayName(lvalue, utils);
}

void ObjectImpl<ServiceGroup>::ValidateGroups(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateGroups(lvalue, utils);
}

void ObjectImpl<ServiceGroup>::ValidateNotes(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateNotes(lvalue, utils);
}

void ObjectImpl<ServiceGroup>::ValidateNotesUrl(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateNotesUrl(lvalue, utils);
}

}
#pragma GCC diagnostic pop
