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

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

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

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

Type::Ptr TypeImpl<InfluxdbWriter>::GetBaseType() const
{
	return InfluxdbCommonWriter::TypeInstance;
}

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

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

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

			break;
		case 112:
			if (name == "password")
				return offset + 2;

			break;
		case 117:
			if (name == "username")
				return offset + 1;

			break;
	}

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

Field TypeImpl<InfluxdbWriter>::GetFieldInfo(int id) const
{
	int real_id = id - InfluxdbCommonWriter::TypeInstance->GetFieldCount();
	if (real_id < 0) { return InfluxdbCommonWriter::TypeInstance->GetFieldInfo(id); }
	switch (real_id) {
		case 0:
			return {0, "String", "database", "database", nullptr, 258, 0};
		case 1:
			return {1, "String", "username", "username", nullptr, 2, 0};
		case 2:
			return {2, "String", "password", "password", nullptr, 2050, 0};
		case 3:
			return {3, "Dictionary", "basic_auth", "basic_auth", nullptr, 2050, 0};
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

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

int TypeImpl<InfluxdbWriter>::GetActivationPriority() const
{
	return 100;
}

void TypeImpl<InfluxdbWriter>::RegisterAttributeHandler(int fieldId, const Type::AttributeHandler& callback)
{
	int real_id = fieldId - InfluxdbCommonWriter::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { InfluxdbCommonWriter::TypeInstance->RegisterAttributeHandler(fieldId, callback); return; }
	switch (real_id) {
		case 0:
			ObjectImpl<InfluxdbWriter>::OnDatabaseChanged.connect(callback);
			break;
		case 1:
			ObjectImpl<InfluxdbWriter>::OnUsernameChanged.connect(callback);
			break;
		case 2:
			ObjectImpl<InfluxdbWriter>::OnPasswordChanged.connect(callback);
			break;
		case 3:
			ObjectImpl<InfluxdbWriter>::OnBasicAuthChanged.connect(callback);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

	if (2 & types)
		ValidateDatabase(Lazy<String>([this]() { return GetDatabase(); }), utils);
	if (2 & types)
		ValidateUsername(Lazy<String>([this]() { return GetUsername(); }), utils);
	if (2 & types)
		ValidatePassword(Lazy<String>([this]() { return GetPassword(); }), utils);
	if (2 & types)
		ValidateBasicAuth(Lazy<Dictionary::Ptr>([this]() { return GetBasicAuth(); }), utils);
}

void ObjectImpl<InfluxdbWriter>::SimpleValidateDatabase(const Lazy<String>& value, const ValidationUtils& utils)
{
	if (value().IsEmpty())
		BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast<ConfigObject *>(this), { "database" }, "Attribute must not be empty."));

}

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

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

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

ObjectImpl<InfluxdbWriter>::ObjectImpl()
{
	SetDatabase(GetDefaultDatabase(), true);
	SetUsername(GetDefaultUsername(), true);
	SetPassword(GetDefaultPassword(), true);
	SetBasicAuth(GetDefaultBasicAuth(), true);
}

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

void ObjectImpl<InfluxdbWriter>::SetField(int id, const Value& value, bool suppress_events, const Value& cookie)
{
	int real_id = id - InfluxdbCommonWriter::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { InfluxdbCommonWriter::SetField(id, value, suppress_events, cookie); return; }
	switch (real_id) {
		case 0:
			SetDatabase(value, suppress_events, cookie);
			break;
		case 1:
			SetUsername(value, suppress_events, cookie);
			break;
		case 2:
			SetPassword(value, suppress_events, cookie);
			break;
		case 3:
			SetBasicAuth(value, suppress_events, cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

Value ObjectImpl<InfluxdbWriter>::GetField(int id) const
{
	int real_id = id - InfluxdbCommonWriter::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { return InfluxdbCommonWriter::GetField(id); }
	switch (real_id) {
		case 0:
			return GetDatabase();
		case 1:
			return GetUsername();
		case 2:
			return GetPassword();
		case 3:
			return GetBasicAuth();
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<InfluxdbWriter>::ValidateField(int id, const Lazy<Value>& lvalue, const ValidationUtils& utils)
{
	int real_id = id - InfluxdbCommonWriter::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { InfluxdbCommonWriter::ValidateField(id, lvalue, utils); return; }
	switch (real_id) {
		case 0:
			ValidateDatabase(lvalue, utils);
			break;
		case 1:
			ValidateUsername(lvalue, utils);
			break;
		case 2:
			ValidatePassword(lvalue, utils);
			break;
		case 3:
			ValidateBasicAuth(lvalue, utils);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

void ObjectImpl<InfluxdbWriter>::NotifyField(int id, const Value& cookie)
{
	int real_id = id - InfluxdbCommonWriter::TypeInstance->GetFieldCount(); 
	if (real_id < 0) { InfluxdbCommonWriter::NotifyField(id, cookie); return; }
	switch (real_id) {
		case 0:
			NotifyDatabase(cookie);
			break;
		case 1:
			NotifyUsername(cookie);
			break;
		case 2:
			NotifyPassword(cookie);
			break;
		case 3:
			NotifyBasicAuth(cookie);
			break;
		default:
			throw std::runtime_error("Invalid field ID.");
	}
}

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

String ObjectImpl<InfluxdbWriter>::GetDatabase() const
{
	return m_Database.load();
}

String ObjectImpl<InfluxdbWriter>::GetUsername() const
{
	return m_Username.load();
}

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

Dictionary::Ptr ObjectImpl<InfluxdbWriter>::GetBasicAuth() const
{
	return m_BasicAuth.load();
}

void ObjectImpl<InfluxdbWriter>::SetDatabase(const String& value, bool suppress_events, const Value& cookie)
{
	m_Database.store(value);
	if (!suppress_events) {
		NotifyDatabase(cookie);
	}

}

void ObjectImpl<InfluxdbWriter>::SetUsername(const String& value, bool suppress_events, const Value& cookie)
{
	m_Username.store(value);
	if (!suppress_events) {
		NotifyUsername(cookie);
	}

}

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

}

void ObjectImpl<InfluxdbWriter>::SetBasicAuth(const Dictionary::Ptr& value, bool suppress_events, const Value& cookie)
{
	m_BasicAuth.store(value);
	if (!suppress_events) {
		NotifyBasicAuth(cookie);
	}

}

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

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

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

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

String ObjectImpl<InfluxdbWriter>::GetDefaultDatabase() const
{
	 return "icinga2"; 
}

String ObjectImpl<InfluxdbWriter>::GetDefaultUsername() const
{
	 return ""; 
}

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

Dictionary::Ptr ObjectImpl<InfluxdbWriter>::GetDefaultBasicAuth() const
{
	return Dictionary::Ptr();
}


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


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


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


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

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

	do {
		if (key != "password")
			break;
		known_attribute = true;
		if (value.IsEmpty() || value.IsScalar())
			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 TIValidateInfluxdbWriterBasicAuth(const intrusive_ptr<ObjectImpl<InfluxdbWriter> >& object, const Dictionary::Ptr& value, std::vector<String>& location, const ValidationUtils& utils)
{
	if (!value)
		return;

	do {
		const 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);
				TIValidateInfluxdbWriter_1(object, akey, avalue, location, utils);
				location.pop_back();
			}
		}
		if (dict->Get("username").IsEmpty())
			BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Required dictionary item 'username' is not set."));
		if (dict->Get("password").IsEmpty())
			BOOST_THROW_EXCEPTION(ValidationError(dynamic_pointer_cast<ConfigObject>(object), location, "Required dictionary item 'password' is not set."));
		return;
	} while (0);

}

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

}

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

}

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

}

void ObjectImpl<InfluxdbWriter>::ValidateBasicAuth(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateBasicAuth(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("basic_auth");
	TIValidateInfluxdbWriterBasicAuth(this, lvalue(), location, utils);
	location.pop_back();
}

void ObjectImpl<InfluxdbWriter>::ValidateDatabase(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateDatabase(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("database");
	TIValidateInfluxdbWriterDatabase(this, lvalue(), location, utils);
	location.pop_back();
}

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

void ObjectImpl<InfluxdbWriter>::ValidateUsername(const Lazy<String>& lvalue, const ValidationUtils& utils)
{
	SimpleValidateUsername(lvalue, utils);
	std::vector<String> location;
	location.emplace_back("username");
	TIValidateInfluxdbWriterUsername(this, lvalue(), location, utils);
	location.pop_back();
}

}
#pragma GCC diagnostic pop
