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

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

#define READ_INTERVAL(c) (c*60*1000UL)	// read interval in minutes

unsigned long currentTime = 0;

RCSwitch mySwitch = RCSwitch();
DHT dht = DHT(DHT11_PIN, DHT11);

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

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.begin();

	Serial.begin(9600);

	delay(1000);
}

void loop() {
	currentTime = millis();
	StaticJsonDocument<200> jsonDoc;
	readCommand();
	readRcSwitch(jsonDoc);
	readDht(jsonDoc);
	if (!jsonDoc.isNull()) {
		serializeJson(jsonDoc, Serial);
		Serial.println();
	}
}

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 (GET_TYPE(value) == SensorType::TEMPERATURE) {
				JsonObject sensor = jsonDoc.createNestedObject("sensor");
				sensor["id"] = ID(value);
				sensor["temperature"] = GET_TEMP(value);
				sensor["voltage"] = GET_VCC(value);
				return;
			}
			if (GET_TYPE(value) == SensorType::HUMIDITY) {
				JsonObject sensor = jsonDoc.createNestedObject("sensor");
				sensor["id"] = ID(value);
				sensor["humidity"] = GET_HUMIDITY(value);
				sensor["voltage"] = GET_VCC(value);
				return;
			}
			if (GET_TYPE(value) == SensorType::CONTACT) {
				JsonObject sensor = jsonDoc.createNestedObject("contact");
				sensor["id"] = ID(value);
				sensor["state"] = GET_STATE(value) ? "on" : "off";
				sensor["voltage"] = GET_VCC(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 runJsonCommand(String cmd) {
	StaticJsonDocument<256> jsonArray;
	DeserializationError err = deserializeJson(jsonArray, cmd);
	if (err == DeserializationError::Ok) {
		JsonArray array = jsonArray.as<JsonArray>();
		for(JsonVariant jsonDoc : array) {
			if (jsonDoc.containsKey("rcSwitch")) {
				JsonObject rcSwitch = jsonDoc["rcSwitch"];
				unsigned int protocol = rcSwitch["protocol"];
				if (protocol == 1) {
					mySwitch.setProtocol(protocol);
					char* group = rcSwitch["group"];
					int channel = rcSwitch["channel"];
					if (rcSwitch["state"]) {
						mySwitch.switchOn(group, channel);
					} else {
						mySwitch.switchOff(group, channel);
					}
				} else {
					mySwitch.setProtocol(protocol);
					mySwitch.send(rcSwitch["value"]);
					// Serial.println((const char*)rcSwitch["value"]);
				}
				blink();
			} else {
				Serial.println(err.c_str());
			}
		}
	}
}

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

void readDht(JsonDocument& jsonDoc) {
	static unsigned long lastReadTime = 0;
	if (currentTime > lastReadTime) {
		lastReadTime = currentTime + READ_INTERVAL(5);
		JsonObject dht11 = jsonDoc.createNestedObject("dht11");
		dht11["temperature"] = dht.readTemperature();
		dht11["humidity"] = dht.readHumidity();
	}
}
