#include "mqttsensorsource.h"

#include<QJsonArray>

MqttSensorSource::MqttSensorSource(QObject *parent)
	: QObject{parent}
{
}

void MqttSensorSource::start(std::shared_ptr<MqttClient> client, const QJsonObject& settings)
{
	this->client = client;

	connect(client->getClient().get(), &QMqttClient::stateChanged, this, &MqttSensorSource::onClientStateChanged);

	QJsonArray sensorsArray = settings["Sensors"].toArray();

	for(QJsonValueRef sensorRef : sensorsArray)
	{
		QJsonObject sensorObject = sensorRef.toObject();
		if(!sensorObject.contains("Topic"))
			continue;
		SensorSubscription sensor;
		sensor.topic = sensorObject["Topic"].toString();
		if(!sensorObject.contains("Name"))
			sensor.name = sensor.topic;
		else
			sensor.name = sensorObject["Name"].toString();
		sensor.id = qHash(client->getBaseTopic() + "/" + sensor.topic);
		sensors.push_back(sensor);
	}
}

void MqttSensorSource::addSensor(const QString& topic, const QString& name)
{
	if(!client)
		return;

	SensorSubscription sensor;
	sensor.topic = topic;
	sensor.name = name;
	sensor.id = qHash(client->getBaseTopic() + "/" + topic);
	sensors.push_back(sensor);

	// Subscribe if already connected
	if(client->getClient()->state() == QMqttClient::ClientState::Connected)
	{
		sensor.subscription = client->subscribe(client->getBaseTopic() + "/" + topic);
		connect(sensor.subscription->subscription, &QMqttSubscription::messageReceived, this, &MqttSensorSource::onMessageReceived);
	}
}

MqttSensorSource::SensorSubscription& MqttSensorSource::findSubscription(const QString& topic)
{
	for(SensorSubscription& sensor : sensors)
	{
		if(client->getBaseTopic() + "/" + sensor.topic == topic)
			return sensor;
	}
	assert(false);
	return sensors.front();
}

void MqttSensorSource::onClientStateChanged(QMqttClient::ClientState state)
{
	if(state == QMqttClient::ClientState::Connected)
	{
		for(SensorSubscription& sensor : sensors)
		{
			sensor.subscription = client->subscribe(client->getBaseTopic() + "/" + sensor.topic);
			connect(sensor.subscription->subscription, &QMqttSubscription::messageReceived, this, &MqttSensorSource::onMessageReceived);
		}
	}
	else if (state == QMqttClient::ClientState::Disconnected)
	{
		for(SensorSubscription& sensor : sensors)
		{
			if(sensor.subscription)
			{
				client->unsubscribe(client->getBaseTopic() + "/" + sensor.topic);
				sensor.subscription = nullptr;
			}
		}
	}
}

void MqttSensorSource::onMessageReceived(const QMqttMessage& message)
{
	Sensor sensor;
	SensorSubscription& sensorSub = findSubscription(message.topic().name());
	QString baseName = sensorSub.name;
	sensor.id = sensorSub.id;
	sensor.updateSeen();

	QJsonDocument doc = QJsonDocument::fromJson(message.payload());
	if(doc.isObject())
	{
		QJsonObject obj = doc.object();
		if(obj.contains("temperature"))
		{
			sensor.name = baseName + " Temperature";
			sensor.type = Sensor::TYPE_TEMPERATURE;
			sensor.field = obj["temperature"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("local_temperature"))
		{
			sensor.name = baseName + " Temperature";
			sensor.type = Sensor::TYPE_TEMPERATURE;
			sensor.field = obj["local_temperature"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("humidity"))
		{
			sensor.name = baseName + " Humidity";
			sensor.type = Sensor::TYPE_HUMIDITY;
			sensor.field = obj["humidity"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("illuminance"))
		{
			sensor.name = baseName + " Illuminance";
			sensor.type = Sensor::TYPE_BRIGHTNESS;
			sensor.field = obj["illuminance"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("presence"))
		{
			sensor.name = baseName + " Presence";
			sensor.type = Sensor::TYPE_OCUPANCY;
			sensor.field = obj["presence"].toBool() ? 1 : 0;
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("co2"))
		{
			sensor.name = baseName + " co2";
			sensor.type = Sensor::TYPE_CO2;
			sensor.field = obj["co2"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("formaldehyd"))
		{
			sensor.name = baseName + " Formaldehyd";
			sensor.type = Sensor::TYPE_FORMALDEHYD;
			sensor.field = obj["formaldehyd"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("pm25"))
		{
			sensor.name = baseName + " pm25";
			sensor.type = Sensor::TYPE_PM25;
			sensor.field = obj["pm25"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("voc"))
		{
			sensor.name = baseName + " VOC";
			sensor.type = Sensor::TYPE_TOTAL_VOC;
			sensor.field = obj["voc"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("power"))
		{
			sensor.name = baseName + " Power";
			sensor.type = Sensor::TYPE_POWER;
			sensor.field = obj["power"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("energy"))
		{
			sensor.name = baseName + " Energy";
			sensor.type = Sensor::TYPE_ENERGY_USE;
			sensor.field = obj["energy"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}

		if(obj.contains("voltage"))
		{
			sensor.name = baseName + " Voltage";
			sensor.type = Sensor::TYPE_VOLTAGE;
			sensor.field = obj["voltage"].toDouble(0);
			stateChanged(sensor, SENSOR_UPDATE_BACKEND);
		}
	}
}

void MqttSensorSource::store(QJsonObject& json)
{
	QJsonArray sensorsArray;
	for(const SensorSubscription& sensor : sensors)
	{
		QJsonObject sensorObject;
		sensorObject["Name"] = sensor.name;
		sensorObject["Topic"] = sensor.topic;
		sensorsArray.append(sensorObject);
	}
	json["Sensors"] = sensorsArray;
}

MqttSensorSource::~MqttSensorSource()
{
	for(SensorSubscription& sub : sensors)
		client->unsubscribe(client->getBaseTopic() + "/" + sub.topic);
}

void MqttSensorSource::onSensorAdded(Sensor sensor, Sensor::sensor_backend_type_t backend, QJsonObject payload)
{
	if(backend != Sensor::BACKEND_MQTT)
		return;

	QString topic = payload["Topic"].toString();
	QString name = payload["Name"].toString();
	if(topic.isEmpty())
		return;

	addSensor(topic, name);
}

