/*
 * Send & receive arbitrary IR codes via a web server or MQTT.
 * Copyright David Conran 2016, 2017, 2018, 2019
 */
#ifndef EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
#define EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_

#include <IRremoteESP8266.h>
#include <IRrecv.h>
#include <IRsend.h>
#include <IRtimer.h>
#include <IRutils.h>
#include <IRac.h>

// ---------------- Start of User Configuration Section ------------------------

#ifndef MQTT_ENABLE
#define MQTT_ENABLE true  // Whether or not MQTT is used at all.
#endif  // MQTT_ENABLE

// ---------------------- Board Related Settings -------------------------------
// NOTE: Make sure you set your Serial Monitor to the same speed.
#define BAUD_RATE 115200  // Serial port Baud rate.

// GPIO the IR LED is connected to/controlled by. GPIO 4 = D2.
#define IR_LED 4  // <=- CHANGE_ME (optional)
// define IR_LED 3  // For an ESP-01 we suggest you use RX/GPIO3/Pin 7.

// GPIO the IR RX module is connected to/controlled by. e.g. GPIO 14 = D5.
// Comment this out to disable receiving/decoding IR messages entirely.
#define IR_RX 14  // <=- CHANGE_ME (optional)
#define IR_RX_PULLUP false

// --------------------- Network Related Settings ------------------------------
const uint16_t kHttpPort = 80;  // The TCP port the HTTP server is listening on.
// Change to 'true'/'false' if you do/don't want these features or functions.
#define USE_STATIC_IP false  // Change to 'true' if you don't want to use DHCP.
// We obtain our network config via DHCP by default but allow an easy way to
// use a static IP config.
#if USE_STATIC_IP
const IPAddress kIPAddress = IPAddress(10, 0, 1, 78);
const IPAddress kGateway = IPAddress(10, 0, 1, 1);
const IPAddress kSubnetMask = IPAddress(255, 255, 255, 0);
#endif  // USE_STATIC_IP

// See: https://github.com/tzapu/WiFiManager#filter-networks for these settings.
#define HIDE_DUPLIATE_NETWORKS false  // Should WifiManager hide duplicate SSIDs
// #define MIN_SIGNAL_STRENGTH 20  // Minimum WiFi signal stength (percentage)
                                   // before we will connect.
                                   // The unset default is 8%.
                                   // (Uncomment to enable)

// ----------------------- HTTP Related Settings -------------------------------
#define FIRMWARE_OTA true  // Allow remote update of the firmware via http.
                           // Less secure if enabled.
                           // Note: Firmware OTA is also disabled until
                           //       a password is set.
#define HTML_PASSWORD_ENABLE false  // Protect access to the HTML interface.
                                    // Note: OTA update is always passworded.
// If you do not set a password, Firmware OTA updates will be blocked.

// ----------------------- MQTT Related Settings -------------------------------
#if MQTT_ENABLE
const uint32_t kMqttReconnectTime = 5000;  // Delay(ms) between reconnect tries.

#define MQTT_ACK "sent"  // Sub-topic we send back acknowledgements on.
#define MQTT_SEND "send"  // Sub-topic we get new commands from.
#define MQTT_RECV "received"  // Topic we send received IRs to.
#define MQTT_LOG "log"  // Topic we send log messages to.
#define MQTT_LWT "status"  // Topic for the Last Will & Testament.
#define MQTT_CLIMATE "ac"  // Sub-topic for the climate topics.
#define MQTT_CLIMATE_CMND "cmnd"  // Sub-topic for the climate command topics.
#define MQTT_CLIMATE_STAT "stat"  // Sub-topic for the climate stat topics.
#define MQTTbroadcastInterval 10 * 60  // Seconds between rebroadcasts

#define QOS 1  // MQTT broker should queue up any unreceived messages for us
// #define QOS 0  // MQTT broker WON'T queue up messages for us. Fire & Forget.
#endif  // MQTT_ENABLE

// ------------------------ IR Capture Settings --------------------------------
// Let's use a larger than normal buffer so we can handle AirCon remote codes.
const uint16_t kCaptureBufferSize = 1024;
#if DECODE_AC
// Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator
// A value this large may swallow repeats of some protocols
const uint8_t kCaptureTimeout = 50;  // Milliseconds
#else  // DECODE_AC
// Suits most messages, while not swallowing many repeats.
const uint8_t kCaptureTimeout = 15;  // Milliseconds
#endif  // DECODE_AC
// Ignore unknown messages with <10 pulses (see also REPORT_UNKNOWNS)
const uint16_t kMinUnknownSize = 2 * 10;
#define REPORT_UNKNOWNS false  // Report inbound IR messages that we don't know.
#define REPORT_RAW_UNKNOWNS false  // Report the whole buffer, recommended:
                                   // MQTT_MAX_PACKET_SIZE of 1024 or more

// ------------------------ Advanced Usage Only --------------------------------
// Change if you need multiple independent send gpio/topics.
const uint8_t gpioTable[] = {
  IR_LED,  // Default GPIO. e.g. ir_server/send or ir_server/send_0
  // Uncomment the following as needed.
  // NOTE: Remember to disable DEBUG if you are using one of the serial pins.
  // 5,  // GPIO 5 / D1 e.g. ir_server/send_1
  // 14,  // GPIO 14 / D5 e.g. ir_server/send_2
  // 16,  // GPIO 16 / D0 e.g. ir_server/send_3
};

#define KEY_PROTOCOL "protocol"
#define KEY_MODEL "model"
#define KEY_POWER "power"
#define KEY_MODE "mode"
#define KEY_TEMP "temp"
#define KEY_FANSPEED "fanspeed"
#define KEY_SWINGV "swingv"
#define KEY_SWINGH "swingh"
#define KEY_QUIET "quiet"
#define KEY_TURBO "turbo"
#define KEY_LIGHT "light"
#define KEY_BEEP "beep"
#define KEY_ECONO "econo"
#define KEY_SLEEP "sleep"
#define KEY_CLOCK "clock"
#define KEY_FILTER "filter"
#define KEY_CLEAN "clean"
#define KEY_CELSIUS "use_celsius"

// HTML arguments we will parse for IR code information.
#define KEY_TYPE "type"  // KEY_PROTOCOL is also checked too.
#define KEY_CODE "code"
#define KEY_BITS "bits"
#define KEY_REPEAT "repeats"

// Text for Last Will & Testament status messages.
const char* kLwtOnline = "Online";
const char* kLwtOffline = "Offline";

const uint8_t kHostnameLength = 30;
const uint8_t kPortLength = 5;  // Largest value of uint16_t is "65535".
const uint8_t kUsernameLength = 15;
const uint8_t kPasswordLength = 20;

// -------------------------- Debug Settings -----------------------------------
// Disable debug output if any of the IR pins are on the TX (D1) pin.
// Note: This is a crude method to catch the common use cases.
// See `isSerialGpioUsedByIr()` for the better method.
#if (IR_LED != 1 && IR_RX != 1)
#ifndef DEBUG
#define DEBUG true  // Change to 'false' to disable all serial output.
#endif  // DEBUG
#else  // (IR_LED != 1 && IR_RX != 1)
#undef DEBUG
#define DEBUG false
#endif

// ----------------- End of User Configuration Section -------------------------

// Constants
#define _MY_VERSION_ "v1.0.0"

const uint8_t kSendTableSize = sizeof(gpioTable);
// JSON stuff
// Name of the json config file in SPIFFS.
const char* kConfigFile = "/config.json";
const char* kMqttServerKey = "mqtt_server";
const char* kMqttPortKey = "mqtt_port";
const char* kMqttUserKey = "mqtt_user";
const char* kMqttPassKey = "mqtt_pass";
const char* kMqttPrefixKey = "mqtt_prefix";
const char* kHostnameKey = "hostname";
const char* kHttpUserKey = "http_user";
const char* kHttpPassKey = "http_pass";

#if MQTT_ENABLE
const uint32_t kBroadcastPeriodMs = MQTTbroadcastInterval * 1000;  // mSeconds.
const uint32_t kStatListenPeriodMs = 5 * 1000;  // mSeconds

void mqttCallback(char* topic, byte* payload, unsigned int length);
String listOfCommandTopics(void);
void handleSendMqttDiscovery(void);
void subscribing(const String topic_name);
void unsubscribing(const String topic_name);
void mqttLog(const String mesg);
bool reconnect(void);
void receivingMQTT(String const topic_name, String const callback_str);
void callback(char* topic, byte* payload, unsigned int length);
void sendMQTTDiscovery(const char *topic);
void doBroadcast(TimerMs *timer, const uint32_t interval,
                 const commonAcState_t state, const bool retain,
                 const bool force);
#endif  // MQTT_ENABLE
bool isSerialGpioUsedByIr(void);
void debug(const char *str);
void saveWifiConfigCallback(void);
void saveWifiConfig(void);
void loadWifiConfigFile(void);
String msToHumanString(uint32_t const msecs);
String timeElapsed(uint32_t const msec);
String timeSince(uint32_t const start);
String listOfSendGpios(void);
bool hasUnsafeHTMLChars(String input);
String htmlMenu(void);
void handleRoot(void);
String addJsReloadUrl(const String url, const uint16_t timeout_s,
                      const bool notify);
void handleExamples(void);
String boolToString(const bool value);
String opmodeToString(const stdAc::opmode_t mode);
String fanspeedToString(const stdAc::fanspeed_t speed);
String swingvToString(const stdAc::swingv_t swingv);
String swinghToString(const stdAc::swingh_t swingh);
String htmlSelectBool(const String name, const bool def);
String htmlSelectProtocol(const String name, const decode_type_t def);
String htmlSelectModel(const String name, const int16_t def);
String htmlSelectMode(const String name, const stdAc::opmode_t def);
String htmlSelectFanspeed(const String name, const stdAc::fanspeed_t def);
String htmlSelectSwingv(const String name, const stdAc::swingv_t def);
String htmlSelectSwingh(const String name, const stdAc::swingh_t def);
void handleAirCon(void);
void handleAirConSet(void);
void handleAdmin(void);
void handleInfo(void);
void handleReset(void);
void handleReboot(void);
bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType,
                              const String str);
uint16_t countValuesInStr(const String str, char sep);
uint16_t * newCodeArray(const uint16_t size);
#if SEND_GLOBALCACHE
bool parseStringAndSendGC(IRsend *irsend, const String str);
#endif  // SEND_GLOBALCACHE
#if SEND_PRONTO
bool parseStringAndSendPronto(IRsend *irsend, const String str,
                              uint16_t repeats);
#endif  // SEND_PRONTO
#if SEND_RAW
bool parseStringAndSendRaw(IRsend *irsend, const String str);
#endif  // SEND_RAW
void handleIr(void);
void handleNotFound(void);
void setup_wifi(void);
void init_vars(void);
void setup(void);
void loop(void);
uint64_t getUInt64fromHex(char const *str);
bool sendIRCode(IRsend *irsend, int const ir_type,
                uint64_t const code, char const * code_str, uint16_t bits,
                uint16_t repeat);
bool sendInt(const String topic, const int32_t num, const bool retain);
bool sendBool(const String topic, const bool on, const bool retain);
bool sendString(const String topic, const String str, const bool retain);
bool sendFloat(const String topic, const float_t temp, const bool retain);
commonAcState_t updateClimate(commonAcState_t current, const String str,
                              const String prefix, const String payload);
bool cmpClimate(const commonAcState_t a, const commonAcState_t b);
bool sendClimate(const commonAcState_t prev, const commonAcState_t next,
                 const String topic_prefix, const bool retain,
                 const bool forceMQTT, const bool forceIR);
#endif  // EXAMPLES_IRMQTTSERVER_IRMQTTSERVER_H_
