/*  
  Theengs OpenMQTTGateway - We Unite Sensors in One Open-Source Interface

   Act as a gateway between your 433mhz, infrared IR, BLE, LoRa signal and one interface like an MQTT broker 
   Send and receiving command by MQTT
 
  This gateway enables to:
 - publish MQTT data to a different topic related to received 433Mhz signal DIO/new kaku protocol

    Copyright: (c)Florian ROBERT
    Copyright: (c)Randy Simons http://randysimons.nl/
  
    This file is part of OpenMQTTGateway.
    
    OpenMQTTGateway is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenMQTTGateway is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*Command example for switching off:
sudo mosquitto_pub -t home/commands/MQTTtoRF2/CODE_8233372/UNIT_0/PERIOD_272 -m 0
Command example for switching on:
sudo mosquitto_pub -t home/commands/MQTTtoRF2/CODE_8233372/UNIT_0/PERIOD_272 -m 1
Command example for dimming:
sudo mosquitto_pub -t home/commands/MQTTtoRF2/CODE_8233372/UNIT_0/PERIOD_272 -m/DIM 8
*/
#include "User_config.h"

#ifdef ZgatewayRF2

#  ifdef ZradioCC1101
#    include <ELECHOUSE_CC1101_SRC_DRV.h>
#  endif

#  include <NewRemoteReceiver.h>
#  include <NewRemoteTransmitter.h>

struct RF2rxd {
  unsigned int period;
  unsigned long address;
  unsigned long groupBit;
  unsigned long unit;
  unsigned long switchType;
  bool hasNewData;
};

RF2rxd rf2rd;

#  ifdef ZmqttDiscovery
//Register for autodiscover in Home Assistant
void RF2toMQTTdiscovery(JsonObject& data) {
  Log.trace(F("switchRF2Discovery" CR));
  String payloadonstr;
  String payloadoffstr;

  int org_switchtype = data["switchType"]; // Store original switchvalue
  data["switchType"] = 1; // switchtype = 1 turns switch on.
  serializeJson(data, payloadonstr);
  data["switchType"] = 0; // switchtype = 0 turns switch off.
  serializeJson(data, payloadoffstr);
  data["switchType"] = org_switchtype; // Restore original switchvalue

  String switchname;
  switchname = "RF2_" + String((int)data["unit"]) + "_" +
               String((int)data["groupbit"]) + "_" +
               String((unsigned long)data["address"]);

  char* switchRF[8] = {"switch",
                       (char*)switchname.c_str(),
                       "",
                       "",
                       "",
                       (char*)payloadonstr.c_str(),
                       (char*)payloadoffstr.c_str(),
                       ""};
  // component type,name,availability topic,device class,value template,payload
  // on, payload off, unit of measurement

  Log.trace(F("CreateDiscoverySwitch: %s" CR), switchRF[1]);

  // As RF2 433Mhz switches do not render their state, no state topic should be
  // provided in the discovery. This will cause the switch to be in optimistic
  // mode in HA with separate on and off icons.
  // The two separate on/off icons allow for subsequent on commands to support
  // the dimming feature of KAKU switches like ACM-300.
  createDiscovery(switchRF[0], "", switchRF[1],
                  (char*)getUniqueId(switchRF[1], "").c_str(), will_Topic,
                  switchRF[3], switchRF[4], switchRF[5], switchRF[6],
                  switchRF[7], 0, "", "", true, subjectMQTTtoRF2,
                  "", "", "", "", false,
                  stateClassNone);
}
#  endif

void RF2toX() {
  if (rf2rd.hasNewData) {
    StaticJsonDocument<JSON_MSG_BUFFER> RF2dataBuffer;
    JsonObject RF2data = RF2dataBuffer.to<JsonObject>();
    rf2rd.hasNewData = false;

    Log.trace(F("Rcv. RF2" CR));
    RF2data["unit"] = (int)rf2rd.unit;
    RF2data["groupBit"] = (int)rf2rd.groupBit;
    RF2data["period"] = (int)rf2rd.period;
    RF2data["address"] = (unsigned long)rf2rd.address;
    RF2data["switchType"] = (int)rf2rd.switchType;
#  ifdef ZmqttDiscovery //component creation for HA
    if (SYSConfig.discovery)
      RF2toMQTTdiscovery(RF2data);
#  endif
    RF2data["origin"] = subjectRF2toMQTT;
    enqueueJsonObject(RF2data);
  }
}

void rf2Callback(unsigned int period, unsigned long address, unsigned long groupBit, unsigned long unit, unsigned long switchType) {
  rf2rd.period = period;
  rf2rd.address = address;
  rf2rd.groupBit = groupBit;
  rf2rd.unit = unit;
  rf2rd.switchType = switchType;
  rf2rd.hasNewData = true;
}

#  if simpleReceiving
void XtoRF2(const char* topicOri, const char* datacallback) {
  disableCurrentReceiver();
  pinMode(RF_EMITTER_GPIO, OUTPUT);
  initCC1101();

  // RF DATA ANALYSIS
  //We look into the subject to see if a special RF protocol is defined
  String topic = topicOri;
  bool boolSWITCHTYPE;
  boolSWITCHTYPE = TheengsUtils::to_bool(datacallback);
  bool isDimCommand = false;

  long valueCODE = 0;
  int valueUNIT = -1;
  int valuePERIOD = 0;
  int valueGROUP = 0;
  int valueDIM = -1;

  int pos = topic.lastIndexOf(RF2codeKey);
  if (pos != -1) {
    pos = pos + +strlen(RF2codeKey);
    valueCODE = (topic.substring(pos, pos + 8)).toInt();
    Log.notice(F("RF2 code: %l" CR), valueCODE);
  }
  int pos2 = topic.lastIndexOf(RF2periodKey);
  if (pos2 != -1) {
    pos2 = pos2 + strlen(RF2periodKey);
    valuePERIOD = (topic.substring(pos2, pos2 + 3)).toInt();
    Log.notice(F("RF2 Period: %d" CR), valuePERIOD);
  }
  int pos3 = topic.lastIndexOf(RF2unitKey);
  if (pos3 != -1) {
    pos3 = pos3 + strlen(RF2unitKey);
    valueUNIT = (topic.substring(pos3, topic.indexOf("/", pos3))).toInt();
    Log.notice(F("Unit: %d" CR), valueUNIT);
  }
  int pos4 = topic.lastIndexOf(RF2groupKey);
  if (pos4 != -1) {
    pos4 = pos4 + strlen(RF2groupKey);
    valueGROUP = (topic.substring(pos4, pos4 + 1)).toInt();
    Log.notice(F("RF2 Group: %d" CR), valueGROUP);
  }
  int pos5 = topic.lastIndexOf(RF2dimKey);
  if (pos5 != -1) {
    isDimCommand = true;
    valueDIM = atoi(datacallback);
    Log.notice(F("RF2 Dim: %d" CR), valueDIM);
  }

  if ((topic == subjectMQTTtoRF2) || (valueCODE != 0) || (valueUNIT != -1) || (valuePERIOD != 0)) {
    Log.trace(F("MQTTtoRF2" CR));
    if (valueCODE == 0)
      valueCODE = 8233378;
    if (valueUNIT == -1)
      valueUNIT = 0;
    if (valuePERIOD == 0)
      valuePERIOD = 272;
    NewRemoteReceiver::disable();
    Log.trace(F("Creating transmitter" CR));
    NewRemoteTransmitter transmitter(valueCODE, RF_EMITTER_GPIO, valuePERIOD, RF2_EMITTER_REPEAT);
    Log.trace(F("Sending data" CR));
    if (valueGROUP) {
      if (isDimCommand) {
        transmitter.sendGroupDim(valueDIM);
      } else {
        transmitter.sendGroup(boolSWITCHTYPE);
      }
    } else {
      if (isDimCommand) {
        transmitter.sendDim(valueUNIT, valueDIM);
      } else {
        transmitter.sendUnit(valueUNIT, boolSWITCHTYPE);
      }
    }
    Log.trace(F("Data sent" CR));
    NewRemoteReceiver::enable();

    // Publish state change back to MQTT
    String MQTTAddress;
    String MQTTperiod;
    String MQTTunit;
    String MQTTgroupBit;
    String MQTTswitchType;
    String MQTTdimLevel;

    MQTTAddress = String(valueCODE);
    MQTTperiod = String(valuePERIOD);
    MQTTunit = String(valueUNIT);
    MQTTgroupBit = String(rf2rd.groupBit);
    MQTTswitchType = String(boolSWITCHTYPE);
    MQTTdimLevel = String(valueDIM);
    String MQTTRF2string;
    Log.trace(F("Adv data XtoRF2 push state via RF2toMQTT" CR));
    if (isDimCommand) {
      MQTTRF2string = subjectRF2toMQTT + String("/") + RF2codeKey + MQTTAddress + String("/") + RF2unitKey + MQTTunit + String("/") + RF2groupKey + MQTTgroupBit + String("/") + RF2dimKey + String("/") + RF2periodKey + MQTTperiod;
      pub((char*)MQTTRF2string.c_str(), (char*)MQTTdimLevel.c_str());
    } else {
      MQTTRF2string = subjectRF2toMQTT + String("/") + RF2codeKey + MQTTAddress + String("/") + RF2unitKey + MQTTunit + String("/") + RF2groupKey + MQTTgroupBit + String("/") + RF2periodKey + MQTTperiod;
      pub((char*)MQTTRF2string.c_str(), (char*)MQTTswitchType.c_str());
    }
  }
#    ifdef ZradioCC1101
  ELECHOUSE_cc1101.SetRx(RFConfig.frequency); // set Receive on
#    endif
  enableActiveReceiver();
}
#  endif

#  if jsonReceiving
void XtoRF2(const char* topicOri, JsonObject& RF2data) { // json object decoding

  if (cmpToMainTopic(topicOri, subjectMQTTtoRF2)) {
    Log.trace(F("MQTTtoRF2 json" CR));
    int boolSWITCHTYPE = RF2data["switchType"] | 99;
    bool success = false;
    if (boolSWITCHTYPE != 99) {
      NewRemoteReceiver::disable();
      pinMode(RF_EMITTER_GPIO, OUTPUT);
      initCC1101();
      Log.trace(F("MQTTtoRF2 switch type ok" CR));
      bool isDimCommand = boolSWITCHTYPE == 2;
      unsigned long valueCODE = RF2data["address"];
      int valueUNIT = RF2data["unit"] | -1;
      int valuePERIOD = RF2data["period"];
      int valueGROUP = RF2data["group"];
      int valueDIM = RF2data["dim"] | -1;
      if ((valueCODE != 0) || (valueUNIT != -1) || (valuePERIOD != 0)) {
        Log.trace(F("MQTTtoRF2" CR));
        if (valueCODE == 0)
          valueCODE = 8233378;
        if (valueUNIT == -1)
          valueUNIT = 0;
        if (valuePERIOD == 0)
          valuePERIOD = 272;
        disableCurrentReceiver();
        NewRemoteTransmitter transmitter(valueCODE, RF_EMITTER_GPIO, valuePERIOD, RF2_EMITTER_REPEAT);
        Log.trace(F("Sending" CR));
        if (valueGROUP) {
          if (isDimCommand) {
            transmitter.sendGroupDim(valueDIM);
          } else {
            transmitter.sendGroup(boolSWITCHTYPE);
          }
        } else {
          if (isDimCommand) {
            transmitter.sendDim(valueUNIT, valueDIM);
          } else {
            transmitter.sendUnit(valueUNIT, boolSWITCHTYPE);
          }
        }
        Log.notice(F("MQTTtoRF2 OK" CR));
        enableActiveReceiver();

        success = true;
      }
    }
    if (success) {
      // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
      RF2data["origin"] = subjectRF2toMQTT;
      enqueueJsonObject(RF2data);
    } else {
      pub(subjectGTWRF2toMQTT, "{\"Status\": \"Error\"}"); // Fail feedback
      Log.error(F("MQTTtoRF2 failed json read" CR));
    }
    enableActiveReceiver();
  }
}
#  endif

void disableRF2Receive() {
  Log.trace(F("disableRF2Receive" CR));
  NewRemoteReceiver::disable();
}

void enableRF2Receive() {
  Log.trace(F("enableRF2Receive" CR));
  NewRemoteReceiver::init(RF_RECEIVER_GPIO, 2, rf2Callback);

  Log.notice(F("RF_EMITTER_GPIO: %d " CR), RF_EMITTER_GPIO);
  Log.notice(F("RF_RECEIVER_GPIO: %d " CR), RF_RECEIVER_GPIO);
  Log.trace(F("ZgatewayRF2 command topic: %s%s%s" CR), mqtt_topic, gateway_name, subjectMQTTtoRF2);
  pinMode(RF_EMITTER_GPIO, OUTPUT);
  digitalWrite(RF_EMITTER_GPIO, LOW);
  Log.trace(F("ZgatewayRF2 setup done " CR));
}

#endif
