/*

RF MODULE

Copyright (C) 2016-2018 by Xose PĂ©rez <xose dot perez at gmail dot com>

*/

#if RF_SUPPORT

#include <RCSwitch.h>

RCSwitch * _rfModem;

unsigned long _rf_learn_start = 0;
unsigned char _rf_learn_id = 0;
bool _rf_learn_status = true;
bool _rf_learn_active = false;

#if WEB_SUPPORT
#include <Ticker.h>
Ticker _rfb_sendcodes;
#endif

// -----------------------------------------------------------------------------
// RF
// -----------------------------------------------------------------------------

unsigned long _rfRetrieve(unsigned char id, bool status) {
    String code = getSetting(status ? "rfbON" : "rfbOFF", id, "0");
    return strtoul(code.c_str(), 0, 16);
}

void _rfStore(unsigned char id, bool status, unsigned long code) {
    DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => %X\n"), id, status ? "ON" : "OFF", code);
    char buffer[20];
    snprintf_P(buffer, sizeof(buffer), PSTR("%X"), code);
    setSetting(status ? "rfbON" : "rfbOFF", id, buffer);
}

void _rfLearn(unsigned char id, bool status) {
    _rf_learn_start = millis();
    _rf_learn_id = id;
    _rf_learn_status = status;
    _rf_learn_active = true;
}

void _rfForget(unsigned char id, bool status) {

    delSetting(status ? "rfbON" : "rfbOFF", id);

    // Websocket update
    #if WEB_SUPPORT
        char wsb[100];
        snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
        wsSend(wsb);
    #endif

}

bool _rfMatch(unsigned long code, unsigned char& relayID, unsigned char& value) {

    bool found = false;
    DEBUG_MSG_P(PSTR("[RF] Trying to match code %X\n"), code);

    for (unsigned char i=0; i<relayCount(); i++) {

        unsigned long code_on = _rfRetrieve(i, true);
        unsigned long code_off = _rfRetrieve(i, false);

        if (code == code_on) {
            DEBUG_MSG_P(PSTR("[RF] Match ON code for relay %d\n"), i);
            value = 1;
            found = true;
        }

        if (code == code_off) {
            DEBUG_MSG_P(PSTR("[RF] Match OFF code for relay %d\n"), i);
            if (found) value = 2;
            found = true;
        }

        if (found) {
            relayID = i;
            return true;
        }

    }

    return false;

}

#if TERMINAL_SUPPORT

void _rfInitCommands() {

    settingsRegisterCommand(F("LEARN"), [](Embedis* e) {

        if (e->argc < 3) {
            DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
            return;
        }
        
        int id = String(e->argv[1]).toInt();
        if (id >= relayCount()) {
            DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
            return;
        }

        int status = String(e->argv[2]).toInt();

        _rfLearn(id, status == 1);

        DEBUG_MSG_P(PSTR("+OK\n"));

    });

    settingsRegisterCommand(F("FORGET"), [](Embedis* e) {

        if (e->argc < 3) {
            DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
            return;
        }
        
        int id = String(e->argv[1]).toInt();
        if (id >= relayCount()) {
            DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
            return;
        }

        int status = String(e->argv[2]).toInt();

        _rfForget(id, status == 1);

        DEBUG_MSG_P(PSTR("+OK\n"));

    });

}

#endif // TERMINAL_SUPPORT

// -----------------------------------------------------------------------------
// WEB
// -----------------------------------------------------------------------------

#if WEB_SUPPORT

void _rfWebSocketSendCode(unsigned char id, bool status, unsigned long code) {
    char wsb[100];
    snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%X\"}]}"), id, status ? 1 : 0, code);
    wsSend(wsb);
}

void _rfWebSocketSendCodes() {
    for (unsigned char id=0; id<relayCount(); id++) {
        _rfWebSocketSendCode(id, true, _rfRetrieve(id, true));
        _rfWebSocketSendCode(id, false, _rfRetrieve(id, false));
    }
}

void _rfWebSocketOnSend(JsonObject& root) {
    char buffer[20];
    root["rfbVisible"] = 1;
    root["rfbCount"] = relayCount();
    _rfb_sendcodes.once_ms(1000, _rfWebSocketSendCodes);
}

void _rfWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
    if (strcmp(action, "rfblearn") == 0) _rfLearn(data["id"], data["status"]);
    if (strcmp(action, "rfbforget") == 0) _rfForget(data["id"], data["status"]);
    if (strcmp(action, "rfbsend") == 0) _rfStore(data["id"], data["status"], strtoul(data["data"], NULL, 16));
}

#endif

// -----------------------------------------------------------------------------

void rfLoop() {

    if (_rfModem->available()) {

        static unsigned long last = 0;
        if (millis() - last > RF_DEBOUNCE) {
            last = millis();

            if (_rfModem->getReceivedValue() > 0) {

                unsigned long rf_code = _rfModem->getReceivedValue();

                DEBUG_MSG_P(PSTR("[RF] Received code: %X\n"), rf_code);

                if (_rf_learn_active) {

                    _rf_learn_active = false;

                    _rfStore(_rf_learn_id, _rf_learn_status, rf_code);

                    // Websocket update
                    #if WEB_SUPPORT
                        _rfWebSocketSendCode(_rf_learn_id, _rf_learn_status, rf_code);
                    #endif

                } else {

                    unsigned char id;
                    unsigned char value;
                    if (_rfMatch(rf_code, id, value)) {
                        if (2 == value) {
                            relayToggle(id);
                        } else {
                            relayStatus(id, 1 == value);
                        }
                    }

                }

            }

        }

        _rfModem->resetAvailable();

    }

    if (_rf_learn_active && (millis() - _rf_learn_start > RF_LEARN_TIMEOUT)) {
        _rf_learn_active = false;
    }

}

void rfSetup() {

    _rfModem = new RCSwitch();
    _rfModem->enableReceive(RF_PIN);
    DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RF_PIN);

    #if WEB_SUPPORT
        wsOnSendRegister(_rfWebSocketOnSend);
        wsOnActionRegister(_rfWebSocketOnAction);
    #endif

     #if TERMINAL_SUPPORT
        _rfInitCommands();
    #endif

   // Register loop
    espurnaRegisterLoop(rfLoop);

}

#endif