/*

ENCODER MODULE

Copyright (C) 2018-2019 by Xose Pérez <xose dot perez at gmail dot com>

*/

#if ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)

#include "libs/Encoder.h"
#include <vector>

struct encoder_t {
    Encoder * encoder;
    unsigned char button_pin;
    unsigned char button_logic;
    unsigned char button_mode;
    unsigned char mode;
    unsigned char channel1;             // default
    unsigned char channel2;             // only if button defined and pressed
};

std::vector<encoder_t> _encoders;
unsigned long _encoder_min_delta = 1;

void _encoderConfigure() {

    _encoder_min_delta = getSetting("encMinDelta", ENCODER_MINIMUM_DELTA).toInt();
    if (!_encoder_min_delta) _encoder_min_delta = 1;

    // no need to reload objects right now
    if (_encoders.size()) return;

    // Clean previous encoders and re-add them
    for (auto& encoder : _encoders) {
        delete encoder.encoder;
    }
    _encoders.clear();

    // TODO: encEnable
    // TODO: implement reloading without re-allocating objects
    #if (ENCODER1_PIN1 != GPIO_NONE) && (ENCODER1_PIN2 != GPIO_NONE)
    {
        _encoders.push_back({
            new Encoder(ENCODER1_PIN1, ENCODER1_PIN2),
            ENCODER1_BUTTON_PIN, ENCODER1_BUTTON_LOGIC, ENCODER1_BUTTON_MODE, ENCODER1_MODE,
            ENCODER1_CHANNEL1, ENCODER1_CHANNEL2
        });
    }
    #endif
    #if (ENCODER2_PIN1 != GPIO_NONE) && (ENCODER2_PIN2 != GPIO_NONE)
    {
        _encoders.push_back({
            new Encoder(ENCODER2_PIN1, ENCODER2_PIN2),
            ENCODER2_BUTTON_PIN, ENCODER2_BUTTON_LOGIC, ENCODER2_BUTTON_MODE, ENCODER2_MODE,
            ENCODER2_CHANNEL1, ENCODER2_CHANNEL2
        });
    }
    #endif
    #if (ENCODER3_PIN1 != GPIO_NONE) && (ENCODER3_PIN2 != GPIO_NONE)
    {
        _encoders.push_back({
            new Encoder(ENCODER3_PIN1, ENCODER3_PIN2),
            ENCODER3_BUTTON_PIN, ENCODER3_BUTTON_LOGIC, ENCODER3_BUTTON_MODE, ENCODER3_MODE,
            ENCODER3_CHANNEL1, ENCODER3_CHANNEL2
        });
    }
    #endif
    #if (ENCODER4_PIN1 != GPIO_NONE) && (ENCODER4_PIN2 != GPIO_NONE)
    {
        _encoders.push_back({
            new Encoder(ENCODER4_PIN1, ENCODER4_PIN2),
            ENCODER4_BUTTON_PIN, ENCODER4_BUTTON_LOGIC, ENCODER4_BUTTON_MODE, ENCODER4_MODE,
            ENCODER4_CHANNEL1, ENCODER4_CHANNEL2
        });
    }
    #endif
    #if (ENCODER5_PIN1 != GPIO_NONE) && (ENCODER5_PIN2 != GPIO_NONE)
    {
        _encoders.push_back({
            new Encoder(ENCODER5_PIN1, ENCODER5_PIN2),
            ENCODER5_BUTTON_PIN, ENCODER5_BUTTON_LOGIC, ENCODER5_BUTTON_MODE, ENCODER5_MODE,
            ENCODER5_CHANNEL1, ENCODER5_CHANNEL2
        });
    }
    #endif

    // TODO: manage buttons through debounceevent?
    for (auto& encoder : _encoders) {
        if (GPIO_NONE != encoder.button_pin) {
            pinMode(encoder.button_pin, encoder.button_mode);
        }
    }

}

void _encoderLoop() {

    // for each encoder, read delta (read()) and map button action
    for (auto& encoder : _encoders) {

        const auto delta = encoder.encoder->read();
        encoder.encoder->write(0);
        if ((0 == delta) || (_encoder_min_delta > abs(delta))) continue;

        if (encoder.button_pin == GPIO_NONE) {

            // if there is no button, the encoder drives CHANNEL1
            lightChannelStep(encoder.channel1, delta);

        } else {

            // otherwise, use button based on encoder mode
            bool pressed = (digitalRead(encoder.button_pin) != encoder.button_logic);

            if (ENCODER_MODE_CHANNEL == encoder.mode) {

                // the button controls what channel we are changing
                lightChannelStep(pressed ? encoder.channel2 : encoder.channel1, delta);

            } if (ENCODER_MODE_RATIO == encoder.mode) {

                // the button controls if we are changing the channel ratio or the overall brightness
                if (pressed) {
                    lightChannelStep(encoder.channel1, delta);
                    lightChannelStep(encoder.channel2, -delta);
                } else {
                    lightBrightnessStep(delta);
                }

            }

        }

        lightUpdate(true, true);

    }

}

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

void encoderSetup() {

    // Configure encoders
    _encoderConfigure();

    // Main callbacks
    espurnaRegisterLoop(_encoderLoop);
    espurnaRegisterReload(_encoderConfigure);

    DEBUG_MSG_P(PSTR("[ENCODER] Number of encoders: %u\n"), _encoders.size());

}

#endif // ENCODER_SUPPORT && (LIGHT_PROVIDER != LIGHT_PROVIDER_NONE)
