#include "mqttclient.h"

MqttClient::MqttClient():
	client(new QMqttClient)
{

}

void MqttClient::start(const QJsonObject& settings)
{
	baseTopicName = settings["BaseTopic"].toString("zigbee2mqtt");

	QMqttClient* cl = client.get();
	connect(cl, &QMqttClient::stateChanged, this, &MqttClient::onClientStateChanged);
	connect(cl, &QMqttClient::errorChanged, this, &MqttClient::onClientError);

	client->setHostname(settings["Host"].toString("127.0.0.1"));
	client->setPort(settings["Port"].toInt(1883));
	client->setClientId(settings["ClientId"].toString("smartvos"));
	if(settings.contains("User"))
		client->setUsername(settings["User"].toString());
	if(settings.contains("Password"))
		client->setPassword(settings["Password"].toString());
	client->setProtocolVersion(QMqttClient::MQTT_3_1);

	client->connectToHost();
}

void MqttClient::onClientError(QMqttClient::ClientError error)
{
	qWarning()<<"MQTT Client error:"<<error;
}

void MqttClient::onClientStateChanged(QMqttClient::ClientState state)
{
	if(state == QMqttClient::ClientState::Connected)
		qInfo()<<"Connected to MQTT broker at "<<client->hostname()<<client->port();
	else if (state == QMqttClient::ClientState::Disconnected)
		qWarning()<<"Lost connection to MQTT broker";
	else if(state == QMqttClient::ClientState::Connecting)
		qInfo()<<"Connecting to MQTT broker at "<<client->hostname()<<client->port();
}

void MqttClient::store(QJsonObject& json)
{
	json["Host"] = client->hostname();
	json["Port"] = client->port();
	json["BaseTopic"] = baseTopicName;
	if(client->username() != "")
		json["User"] = client->username();
	if(client->password() != "")
		json["Password"] = client->password();
}

std::shared_ptr<QMqttClient> MqttClient::getClient()
{
	return client;
}

MqttClient::Subscription* MqttClient::subscribe(QString topic)
{
	if(subscriptions.contains(topic))
	{
		MqttClient::Subscription* sub = subscriptions[topic];
		++sub->ref;
		return sub;
	}
	else
	{
		qDebug()<<"MqttClient: subscibeing to"<<topic;
		MqttClient::Subscription* sub = new Subscription;
		sub->subscription = client->subscribe(topic);
		sub->ref = 1;
		subscriptions.insert({topic, sub});
		return sub;
	}
}

void MqttClient::unsubscribe(MqttClient::Subscription* subscription)
{
	QString topic = subscription->subscription->topic().filter();
	unsubscribe(topic);
}

void MqttClient::unsubscribe(QString topic)
{
	MqttClient::Subscription* sub = subscriptions[topic];
	if(!sub)
	{
		qWarning()<<"MqttClient: Trying to unsubscribe from unkown topic:"<<topic;
			return;
	}

	if(--sub->ref > 0)
		return;

	qDebug()<<"MqttClient: unsubscibeing"<<sub->subscription->topic().filter();
	client->unsubscribe(sub->subscription->topic());
	subscriptions.erase(topic);
	delete sub;
}

QString MqttClient::getBaseTopic()
{
	return baseTopicName;
}

MqttClient::~MqttClient()
{
	for(const std::pair<QString, Subscription*> sub : subscriptions)
	{
		qWarning()<<sub.first<<"not unregistered at exit!";
		client->unsubscribe(sub.second->subscription->topic());
	}
}