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

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

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

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

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

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

	switch (static_cast<int>(Utility::SDBM(name, 1))) {
		case 99:
			if (name == "client_cn")
				return offset + 2;

			break;
		case 112:
			if (name == "password")
				return offset + 0;
			if (name == "password_hash")
				return offset + 1;
			if (name == "permissions")
				return offset + 3;

			break;
	}

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

Field TypeImpl<ApiUser>::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, "String", "password", "password", nullptr, 2050, 0};
		case 1:
			return {1, "String", "password_hash", "password_hash", nullptr, 6146, 0};
		case 2:
			return {2, "String", "client_cn", "client_cn", nullptr, 2, 0};
		case 3:
			return {3, "Array", "permissions", "permissions", nullptr, 2, 1};
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

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

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

void TypeImpl<ApiUser>::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<ApiUser>::OnPasswordChanged.connect(callback);
			break;
		case 1:
			ObjectImpl<ApiUser>::OnPasswordHashChanged.connect(callback);
			break;
		case 2:
			ObjectImpl<ApiUser>::OnClientCNChanged.connect(callback);
			break;
		case 3:
			ObjectImpl<ApiUser>::OnPermissionsChanged.connect(callback);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

	if (2 & types)
		ValidatePassword(Lazy<String>([this]() { return GetPassword(); }), utils);
	if (2 & types)
		ValidatePasswordHash(Lazy<String>([this]() { return GetPasswordHash(); }), utils);
	if (2 & types)
		ValidateClientCN(Lazy<String>([this]() { return GetClientCN(); }), utils);
	if (2 & types)
		ValidatePermissions(Lazy<Array::Ptr>([this]() { return GetPermissions(); }), utils);
}

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

void ObjectImpl<ApiUser>::SimpleValidatePasswordHash(const Lazy<String>& value, const ValidationUtils& utils)
{
	if (value() != GetDefaultPasswordHash())
		Log(LogWarning, "ApiUser") << "Attribute 'password_hash' for object '" << dynamic_cast<ConfigObject *>(this)->GetName() << "' of type '" << dynamic_cast<ConfigObject *>(this)->GetReflectionType()->GetName() << "' is deprecated and should not be used.";
}

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

void ObjectImpl<ApiUser>::SimpleValidatePermissions(const Lazy<Array::Ptr>& avalue, const ValidationUtils& utils)
{
	if (avalue()) {
		ObjectLock olock(avalue());
		for (const Value& value : avalue()) {
	if (value.IsObjectType<Function>()) {
		Function::Ptr func = value;
		if (func->IsDeprecated())
			Log(LogWarning, "ApiUser") << "Attribute 'permissions' for object '" << dynamic_cast<ConfigObject *>(this)->GetName() << "' of type '" << dynamic_cast<ConfigObject *>(this)->GetReflectionType()->GetName() << "' is set to a deprecated function: " << func->GetName();
	}

		}
	}
}

ObjectImpl<ApiUser>::ObjectImpl()
{
	SetPassword(GetDefaultPassword(), true);
	SetPasswordHash(GetDefaultPasswordHash(), true);
	SetClientCN(GetDefaultClientCN(), true);
	SetPermissions(GetDefaultPermissions(), true);
}

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

void ObjectImpl<ApiUser>::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:
			SetPassword(value, suppress_events, cookie);
			break;
		case 1:
			SetPasswordHash(value, suppress_events, cookie);
			break;
		case 2:
			SetClientCN(value, suppress_events, cookie);
			break;
		case 3:
			SetPermissions(value, suppress_events, cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

Value ObjectImpl<ApiUser>::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 GetPassword();
		case 1:
			return GetPasswordHash();
		case 2:
			return GetClientCN();
		case 3:
			return GetPermissions();
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<ApiUser>::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:
			ValidatePassword(lvalue, utils);
			break;
		case 1:
			ValidatePasswordHash(lvalue, utils);
			break;
		case 2:
			ValidateClientCN(lvalue, utils);
			break;
		case 3:
			ValidatePermissions(lvalue, utils);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<ApiUser>::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:
			NotifyPassword(cookie);
			break;
		case 1:
			NotifyPasswordHash(cookie);
			break;
		case 2:
			NotifyClientCN(cookie);
			break;
		case 3:
			NotifyPermissions(cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

Object::Ptr ObjectImpl<ApiUser>::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.");
}

String ObjectImpl<ApiUser>::GetPassword() const
{
	return m_Password.load();
}

String ObjectImpl<ApiUser>::GetPasswordHash() const
{
	return m_PasswordHash.load();
}

String ObjectImpl<ApiUser>::GetClientCN() const
{
	return m_ClientCN.load();
}

Array::Ptr ObjectImpl<ApiUser>::GetPermissions() const
{
	return m_Permissions.load();
}

void ObjectImpl<ApiUser>::SetPassword(const String& value, bool suppress_events, const Value& cookie)
{
	m_Password.store(value);
	if (!suppress_events) {
		NotifyPassword(cookie);
	}

}

void ObjectImpl<ApiUser>::SetPasswordHash(const String& value, bool suppress_events, const Value& cookie)
{
	m_PasswordHash.store(value);
	if (!suppress_events) {
		NotifyPasswordHash(cookie);
	}

}

void ObjectImpl<ApiUser>::SetClientCN(const String& value, bool suppress_events, const Value& cookie)
{
	m_ClientCN.store(value);
	if (!suppress_events) {
		NotifyClientCN(cookie);
	}

}

void ObjectImpl<ApiUser>::SetPermissions(const Array::Ptr& value, bool suppress_events, const Value& cookie)
{
	m_Permissions.store(value);
	if (!suppress_events) {
		NotifyPermissions(cookie);
	}

}

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

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

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

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

String ObjectImpl<ApiUser>::GetDefaultPassword() const
{
	return String();
}

String ObjectImpl<ApiUser>::GetDefaultPasswordHash() const
{
	return String();
}

String ObjectImpl<ApiUser>::GetDefaultClientCN() const
{
	return String();
}

Array::Ptr ObjectImpl<ApiUser>::GetDefaultPermissions() const
{
	return Array::Ptr();
}


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


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


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


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

static void TIValidateApiUser_1_2(const intrusive_ptr<ObjectImpl<ApiUser> >& object, const String& key, const Value& value, std::vector<String>& location, const ValidationUtils& utils)
{
	bool known_attribute = false;
	do {
		if (key != "permission")
			break;
		known_attribute = true;
		if (value.IsEmpty() || value.IsScalar())
			return;
	} while (0);

	do {
		if (key != "filter")
			break;
		known_attribute = true;
		if (value.IsObjectType<Function>()) {
			return;
		}
	} while (0);

	if (!known_attribute)
		BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Invalid attribute: " + key));
	else
		BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Invalid type."));
}

static void TIValidateApiUser_1(const intrusive_ptr<ObjectImpl<ApiUser> >& object, const String& key, const Value& value, std::vector<String>& location, const ValidationUtils& utils)
{
	bool known_attribute = false;
	do {
		known_attribute = true;
		if (value.IsEmpty() || value.IsScalar())
			return;
	} while (0);

	do {
		known_attribute = true;
		if (value.IsObjectType<Dictionary>()) {
			Dictionary::Ptr dict = value;
			{
				ObjectLock olock(dict);
				for (const Dictionary::Pair& kv : dict) {
					const String& akey = kv.first;
					const Value& avalue = kv.second;
					location.emplace_back(akey);
					TIValidateApiUser_1_2(object, akey, avalue, location, utils);
					location.pop_back();
				}
			}
			if (dict->Get("permission").IsEmpty())
				BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Required dictionary item 'permission' is not set."));
			return;
		}
	} while (0);

	if (!known_attribute)
		BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Invalid attribute: " + key));
	else
		BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Invalid type."));
}

static void TIValidateApiUserClientCN(const intrusive_ptr<ObjectImpl<ApiUser> >& object, const String& value, std::vector<String>& location, const ValidationUtils& utils)
{
	if (value.IsEmpty())
		return;

}

static void TIValidateApiUserPassword(const intrusive_ptr<ObjectImpl<ApiUser> >& object, const String& value, std::vector<String>& location, const ValidationUtils& utils)
{
	if (value.IsEmpty())
		return;

}

static void TIValidateApiUserPasswordHash(const intrusive_ptr<ObjectImpl<ApiUser> >& object, const String& value, std::vector<String>& location, const ValidationUtils& utils)
{
	if (value.IsEmpty())
		return;

}

static void TIValidateApiUserPermissions(const intrusive_ptr<ObjectImpl<ApiUser> >& object, const Array::Ptr& value, std::vector<String>& location, const ValidationUtils& utils)
{
	if (!value)
		return;

	do {
		const Array::Ptr& arr = value;
		Array::SizeType anum = 0;
		{
			ObjectLock olock(arr);
			for (const Value& avalue : arr) {
				String akey = Convert::ToString(anum);
				location.emplace_back(akey);
				TIValidateApiUser_1(object, akey, avalue, location, utils);
				location.pop_back();
				anum++;
			}
		}
		return;
	} while (0);

}

void ObjectImpl<ApiUser>::ValidateClientCN(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateClientCN(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("client_cn");
	TIValidateApiUserClientCN(this, lvalue(), location, utils);
	location.pop_back();
}

void ObjectImpl<ApiUser>::ValidatePassword(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidatePassword(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("password");
	TIValidateApiUserPassword(this, lvalue(), location, utils);
	location.pop_back();
}

void ObjectImpl<ApiUser>::ValidatePasswordHash(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidatePasswordHash(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("password_hash");
	TIValidateApiUserPasswordHash(this, lvalue(), location, utils);
	location.pop_back();
}

void ObjectImpl<ApiUser>::ValidatePermissions(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
{
	SimpleValidatePermissions(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("permissions");
	TIValidateApiUserPermissions(this, lvalue(), location, utils);
	location.pop_back();
}

}
#pragma GCC diagnostic pop
