#include <Arduino.h>
#include <RCSwitch.h>
#include <Adafruit_Sensor.h>
#include "Tiny.h"
#include <ArduinoJson.h>
#include "Dht.h"


#define RESET_PIN 10
#define SEND_PIN 11
#define RECEIVE_PIN 2


RCSwitch mySwitch = RCSwitch();

void readRcSwitch(JsonDocument& jsonDoc);
void readCommand();

void setup() {
	digitalWrite(RESET_PIN, HIGH);
	pinMode(LED_BUILTIN, OUTPUT);
	pinMode(RESET_PIN, OUTPUT);

	mySwitch.enableReceive(digitalPinToInterrupt(RECEIVE_PIN));
	mySwitch.enableTransmit(SEND_PIN);
	mySwitch.setRepeatTransmit(10);

	Dht::setup();

	Serial.begin(9600);

	delay(1000);
}

void loop() {
	readCommand();
	StaticJsonDocument<200> jsonDoc;
	readRcSwitch(jsonDoc);
	Dht::read(jsonDoc);
	if (!jsonDoc.isNull()) {
		serializeJson(jsonDoc, Serial);
		Serial.println();
	}
}

bool buildSensorJson(JsonDocument& jsonDoc, unsigned long value) {
	jsonDoc["id"] = ID(value);

	float voltage = (float)GET_VCC(value) / 1000;
	if (voltage != 0) {
		JsonObject diagnostic = jsonDoc.createNestedObject("diagnostic");
		diagnostic["voltage"] = voltage;
	}

	JsonObject sensor = jsonDoc.createNestedObject("sensor");
	switch (GET_TYPE(value)) {
	case SensorType::GENERIC:
		sensor["value"] = GET_VALUE(value);
		break;
	case SensorType::TEMPERATURE:
		sensor["temperature"] = (float)GET_TEMP(value) / 10;
		break;
	case SensorType::HUMIDITY:
		sensor["humidity"] = (float)GET_HUMIDITY(value) / 10;
		break;
	case SensorType::CONTACT:
		sensor["state"] = GET_STATE(value) ? "on" : "off";
		break;
	default:
		return false;
	}

	return true;
}

void readRcSwitch(JsonDocument& jsonDoc) {
	if (mySwitch.available()) {
		unsigned long value = mySwitch.getReceivedValue();
		mySwitch.resetAvailable();

		if (mySwitch.getReceivedProtocol() == 2) {
			if (value == 637541753L || value == 771759481L) {
				JsonObject motion = jsonDoc.createNestedObject("motion");
				motion["kitchen"] = value == 637541753L ? "on" : "off";
				return;
			}
			if (value == 1879048230L || value == 1879048198L) {
				JsonObject motion = jsonDoc.createNestedObject("motion");
				motion["basement"] = value == 1879048230L ? "on" : "off";
				return;
			}
			if (buildSensorJson(jsonDoc, value)) {
				return;
			}
		}
		JsonObject rcSwitch = jsonDoc.createNestedObject("rcSwitch");
		rcSwitch["protocol"] = mySwitch.getReceivedProtocol();
		rcSwitch["value"] = value;
	}
}

void blink() {
	digitalWrite(LED_BUILTIN, HIGH);
	delay(200);
	digitalWrite(LED_BUILTIN, LOW);
}

void runRcSwitchCommand(JsonVariant jsonDoc) {
	JsonObject rcSwitch = jsonDoc["rcSwitch"];
	unsigned int protocol = rcSwitch["protocol"];
	if (protocol == 1) {
		mySwitch.setProtocol(protocol);
		char* group = rcSwitch["group"];
		int channel = rcSwitch["channel"];
		rcSwitch["state"] ? mySwitch.switchOn(group, channel) : mySwitch.switchOff(group, channel);
	} else {
		mySwitch.setProtocol(protocol);
		mySwitch.send(rcSwitch["value"]);
	}
	serializeJson(jsonDoc, Serial);
	Serial.println();
	// blink();
}

void runJsonCommands(const char* cmd) {
	String origCmd = String(cmd);
	StaticJsonDocument<512> jsonArray;
	DeserializationError err = deserializeJson(jsonArray, cmd);
	if (err == DeserializationError::Ok) {
		JsonArray array = jsonArray.as<JsonArray>();
		for (JsonVariant jsonDoc : array) {
			if (jsonDoc.containsKey("rcSwitch")) {
				runRcSwitchCommand(jsonDoc);
			}
		}
	} else {
		Serial.print(err.c_str());
		Serial.print(": ");
		Serial.println(origCmd);
	}
}

void readCommand() {
	if (Serial.available() > 0) {
		String cmd = Serial.readStringUntil('\n');
		if (cmd == "reset") {
			Serial.println("resetting...");
			delay(200);
			digitalWrite(RESET_PIN, LOW);
			Serial.println("resetting failed");
		}
		if (cmd.endsWith(",")) {
			cmd = cmd.substring(0, cmd.lastIndexOf(','));
		}
		cmd = "[" + cmd + "]";
		runJsonCommands(cmd.c_str());
	}
}
