/*
MY92XX LED Driver for Arduino
Based on the C driver by MaiKe Labs
Copyright (c) 2016 - 2026 MaiKe Labs
Copyright (C) 2017 - 2018 Xose Pérez for the Arduino compatible library
This program 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.
This program 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 .
*/
#ifndef _my92xx_h
#define _my92xx_h
#include 
#ifdef DEBUG_MY92XX
#if ARDUINO_ARCH_ESP8266
#define DEBUG_MSG_MY92XX(...) DEBUG_MY92XX.printf( __VA_ARGS__ )
#elif ARDUINO_ARCH_AVR
#define DEBUG_MSG_MY92XX(...) { char buffer[80]; snprintf(buffer, sizeof(buffer),  __VA_ARGS__ ); DEBUG_MY92XX.print(buffer); }
#endif
#else
#define DEBUG_MSG_MY92XX(...)
#endif
typedef enum my92xx_model_t {
    MY92XX_MODEL_MY9291 = 0X00,
    MY92XX_MODEL_MY9231 = 0X01,
} my92xx_model_t;
typedef enum my92xx_cmd_one_shot_t {
    MY92XX_CMD_ONE_SHOT_DISABLE = 0X00,
    MY92XX_CMD_ONE_SHOT_ENFORCE = 0X01,
} my92xx_cmd_one_shot_t;
typedef enum my92xx_cmd_reaction_t {
    MY92XX_CMD_REACTION_FAST = 0X00,
    MY92XX_CMD_REACTION_SLOW = 0X01,
} my92xx_cmd_reaction_t;
typedef enum my92xx_cmd_bit_width_t {
    MY92XX_CMD_BIT_WIDTH_16 = 0X00,
    MY92XX_CMD_BIT_WIDTH_14 = 0X01,
    MY92XX_CMD_BIT_WIDTH_12 = 0X02,
    MY92XX_CMD_BIT_WIDTH_8 = 0X03,
} my92xx_cmd_bit_width_t;
typedef enum my92xx_cmd_frequency_t {
    MY92XX_CMD_FREQUENCY_DIVIDE_1 = 0X00,
    MY92XX_CMD_FREQUENCY_DIVIDE_4 = 0X01,
    MY92XX_CMD_FREQUENCY_DIVIDE_16 = 0X02,
    MY92XX_CMD_FREQUENCY_DIVIDE_64 = 0X03,
} my92xx_cmd_frequency_t;
typedef enum my92xx_cmd_scatter_t {
    MY92XX_CMD_SCATTER_APDM = 0X00,
    MY92XX_CMD_SCATTER_PWM = 0X01,
} my92xx_cmd_scatter_t;
typedef struct {
    my92xx_cmd_scatter_t scatter : 1;
    my92xx_cmd_frequency_t frequency : 2;
    my92xx_cmd_bit_width_t bit_width : 2;
    my92xx_cmd_reaction_t reaction : 1;
    my92xx_cmd_one_shot_t one_shot : 1;
    unsigned char resv : 1;
} __attribute__((aligned(1), packed)) my92xx_cmd_t;
#define MY92XX_COMMAND_DEFAULT { \
    .scatter = MY92XX_CMD_SCATTER_APDM, \
    .frequency = MY92XX_CMD_FREQUENCY_DIVIDE_1, \
    .bit_width = MY92XX_CMD_BIT_WIDTH_8, \
    .reaction = MY92XX_CMD_REACTION_FAST, \
    .one_shot = MY92XX_CMD_ONE_SHOT_DISABLE, \
    .resv = 0 \
}
class my92xx {
public:
    my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command);
    unsigned char getChannels();
    void setChannel(unsigned char channel, unsigned int value);
    unsigned int getChannel(unsigned char channel);
    void setState(bool state);
    bool getState();
    void update();
private:
    void _di_pulse(unsigned int times);
    void _dcki_pulse(unsigned int times);
    void _set_cmd(my92xx_cmd_t command);
    void _send();
    void _write(unsigned int data, unsigned char bit_length);
    my92xx_cmd_t _command;
    my92xx_model_t _model = MY92XX_MODEL_MY9291;
    unsigned char _chips = 1;
    unsigned char _channels;
    uint16_t* _value;
    bool _state = false;
    unsigned char _pin_di;
    unsigned char _pin_dcki;
};
#if ARDUINO_ARCH_ESP8266
extern "C" {
    void os_delay_us(unsigned int);
}
#elif ARDUINO_ARCH_AVR
#define os_delay_us delayMicroseconds
#endif
void my92xx::_di_pulse(unsigned int times) {
    for (unsigned int i = 0; i < times; i++) {
        digitalWrite(_pin_di, HIGH);
        digitalWrite(_pin_di, LOW);
    }
}
void my92xx::_dcki_pulse(unsigned int times) {
    for (unsigned int i = 0; i < times; i++) {
        digitalWrite(_pin_dcki, HIGH);
        digitalWrite(_pin_dcki, LOW);
    }
}
void my92xx::_write(unsigned int data, unsigned char bit_length) {
    unsigned int mask = (0x01 << (bit_length - 1));
    for (unsigned int i = 0; i < bit_length / 2; i++) {
        digitalWrite(_pin_dcki, LOW);
        digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
        digitalWrite(_pin_dcki, HIGH);
        data = data << 1;
        digitalWrite(_pin_di, (data & mask) ? HIGH : LOW);
        digitalWrite(_pin_dcki, LOW);
        digitalWrite(_pin_di, LOW);
        data = data << 1;
    }
}
void my92xx::_set_cmd(my92xx_cmd_t command) {
    // ets_intr_lock();
    // TStop > 12us.
    os_delay_us(12);
    // Send 12 DI pulse, after 6 pulse's falling edge store duty data, and 12
    // pulse's rising edge convert to command mode.
    _di_pulse(12);
    // Delay >12us, begin send CMD data
    os_delay_us(12);
    // Send CMD data
    unsigned char command_data = *(unsigned char*)(&command);
    for (unsigned char i = 0; i < _chips; i++) {
        _write(command_data, 8);
    }
    // TStart > 12us. Delay 12 us.
    os_delay_us(12);
    // Send 16 DI pulse,at 14 pulse's falling edge store CMD data, and
    // at 16 pulse's falling edge convert to duty mode.
    _di_pulse(16);
    // TStop > 12us.
    os_delay_us(12);
    // ets_intr_unlock();
}
void my92xx::_send() {
#ifdef DEBUG_MY92XX
    DEBUG_MSG_MY92XX("[MY92XX] Refresh: %s (", _state ? "ON" : "OFF");
    for (unsigned char channel = 0; channel < _channels; channel++) {
        DEBUG_MSG_MY92XX(" %d", _value[channel]);
    }
    DEBUG_MSG_MY92XX(" )\n");
#endif
    unsigned char bit_length = 8;
    switch (_command.bit_width) {
    case MY92XX_CMD_BIT_WIDTH_16:
        bit_length = 16;
        break;
    case MY92XX_CMD_BIT_WIDTH_14:
        bit_length = 14;
        break;
    case MY92XX_CMD_BIT_WIDTH_12:
        bit_length = 12;
        break;
    case MY92XX_CMD_BIT_WIDTH_8:
        bit_length = 8;
        break;
    default:
        bit_length = 8;
        break;
    }
    // ets_intr_lock();
    // TStop > 12us.
    os_delay_us(12);
    // Send color data
    for (unsigned char channel = 0; channel < _channels; channel++) {
        _write(_state ? _value[channel] : 0, bit_length);
    }
    // TStart > 12us. Ready for send DI pulse.
    os_delay_us(12);
    // Send 8 DI pulse. After 8 pulse falling edge, store old data.
    _di_pulse(8);
    // TStop > 12us.
    os_delay_us(12);
    // ets_intr_unlock();
}
// -----------------------------------------------------------------------------
unsigned char my92xx::getChannels() {
    return _channels;
}
void my92xx::setChannel(unsigned char channel, unsigned int value) {
    if (channel < _channels) {
        _value[channel] = value;
    }
}
unsigned int my92xx::getChannel(unsigned char channel) {
    if (channel < _channels) {
        return _value[channel];
    }
    return 0;
}
bool my92xx::getState() {
    return _state;
}
void my92xx::setState(bool state) {
    _state = state;
}
void my92xx::update() {
    _send();
}
// -----------------------------------------------------------------------------
my92xx::my92xx(my92xx_model_t model, unsigned char chips, unsigned char di, unsigned char dcki, my92xx_cmd_t command) : _command(command) {
    _model = model;
    _chips = chips;
    _pin_di = di;
    _pin_dcki = dcki;
    // Init channels
    if (_model == MY92XX_MODEL_MY9291) {
        _channels = 4 * _chips;
    }
    else if (_model == MY92XX_MODEL_MY9231) {
        _channels = 3 * _chips;
    }
    _value = new uint16_t[_channels];
    for (unsigned char i = 0; i < _channels; i++) {
        _value[i] = 0;
    }
    // Init GPIO
    pinMode(_pin_di, OUTPUT);
    pinMode(_pin_dcki, OUTPUT);
    digitalWrite(_pin_di, LOW);
    digitalWrite(_pin_dcki, LOW);
    // Clear all duty register
    _dcki_pulse(32 * _chips);
    // Send init command
    _set_cmd(command);
    DEBUG_MSG_MY92XX("[MY92XX] Initialized\n");
}
#endif