/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#include <eCxx/Platform/MAX72XXSevSegDriver.h>


namespace eCxx {


/*
 * Character map
 *
 *          ╭───────╮             Byte    : 76543210
 *          │   A   │             Segment : XABCDEFG (X = Dot)
 *          ╰───────╯
 *     ╭───╮         ╭───╮
 *     │   │         │   │
 *     │ F │         │ B │
 *     │   │         │   │
 *     ╰───╯         ╰───╯
 *          ╭───────╮
 *          │   G   │
 *          ╰───────╯
 *     ╭───╮         ╭───╮
 *     │   │         │   │
 *     │ E │         │ C │
 *     │   │         │   │
 *     ╰───╯         ╰───╯
 *          ╭───────╮     ╭───╮
 *          │   D   │     │Dot│
 *          ╰───────╯     ╰───╯
 */
static constexpr uint8_t SSCM_Invalid   = 0b11111111;

static constexpr uint8_t SSCM_Dot_Mask  = 0b10000000;

                                         // XABCDEFG
static constexpr uint8_t SSCM_Chr_Minus = 0b00000001;
static constexpr uint8_t SSCM_Chr_L     = 0b00001110;
static constexpr uint8_t SSCM_Chr_H     = 0b00110111;
static constexpr uint8_t SSCM_Chr_P     = 0b01100111;
static constexpr uint8_t SSCM_Chr_Space = 0b00000000;

static constexpr uint8_t SSCM_Chr_09[10] = {
   // XABCDEFG
    0b01111110, // 0
    0b00110000, // 1
    0b01101101, // 2
    0b01111001, // 3
    0b00110011, // 4
    0b01011011, // 5
    0b01011111, // 6
    0b01110000, // 7
    0b01111111, // 8
    0b01111011  // 9
};

static constexpr uint8_t SSCM_Chr_AF[10] = {
   // XABCDEFG
    0b01110111, // A
    0b00011111, // b
    0b00001101, // c
    0b00111101, // d
    0b01001111, // E
    0b01000111  // F
};


//
// A simple MAX7219/MAX7221-based 7-segment driver
//
bool MAX72XXSevenSegmentDriver::begin(uint8_t pinData, uint8_t pinClock, uint8_t pinLatch, uint8_t numMod, uint8_t* byteBuffer, bool reverseDigitDirection)
{
    // Check if it is already initialized or the byte buffer is nullptr
    if( rawByteBuffer() || !byteBuffer ) return false;

    // Save the settings
    _reverseDigitDir = reverseDigitDirection;

    // Clear all digits
    clearAllDigits();

    // Initialize the driver
    return MAX72XXMatrixDriver::begin(
        pinData,
        pinClock,
        pinLatch,
        numMod * DigitPerModule,
        8,
        byteBuffer
    );
}


void MAX72XXSevenSegmentDriver::setRawByteBuffer(const uint8_t* rawByteBuffer_)
{
    // Check if it is not yet initialized
    if( !rawByteBuffer() ) return;

    // Check if the new raw byte buffer is nullptr
    if(!rawByteBuffer_) return;

    // Save the new raw byte buffer pointer
    MAX72XXMatrixDriver::setRawByteBuffer(rawByteBuffer_);
}


bool MAX72XXSevenSegmentDriver::setDigit(uint8_t index, char value, bool dot)
{
    // Check if it is not yet initialized
    if( !rawByteBuffer() ) return false;

    // Set the result as an invalid value first
    uint8_t digit = SSCM_Invalid;

    // Map the value to the appropriate 7-segment character
         if(value >= '0' && value <= '9') digit = SSCM_Chr_09[value - '0'];
    else if(value >= 'a' && value <= 'f') digit = SSCM_Chr_AF[value - 'a'];
    else if(value >= 'A' && value <= 'F') digit = SSCM_Chr_AF[value - 'A'];
    else if(value == '-'                ) digit = SSCM_Chr_Minus;
    else if(value == 'l' || value == 'L') digit = SSCM_Chr_L;
    else if(value == 'h' || value == 'H') digit = SSCM_Chr_H;
    else if(value == 'p' || value == 'P') digit = SSCM_Chr_P;
    else if(value == ' '                ) digit = SSCM_Chr_Space;

    // It was a valid value
    if(digit != SSCM_Invalid) {
        // Add the dot as needed
        if(dot) digit |= SSCM_Dot_Mask;
        // Send the digit
        setCustomDigit(index, digit);
        // Done
        return true;
    }

    // It was an invalid value
    return false;
}


void MAX72XXSevenSegmentDriver::setCustomDigit(uint8_t index, uint8_t value)
{
    // Check if it is not yet initialized
    if( !rawByteBuffer() ) return;

    // Hardware
    //                   leftmost-digit                  rightmost-digit
    //     digit flow  : 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    //     memory flow : 15 07 14 06 13 05 12 04 11 03 10 02 09 01 08 00
    //
    // User interface
    //                   leftmost-digit                  rightmost-digit
    //     index       : 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
    //     modNum      :  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1
    //     segNum      :  0  1  2  3  4  5  6  7  0  1  2  3  4  5  6  7

    const uint8_t modNum = index / DigitPerModule;
    const uint8_t segNum = index % DigitPerModule;

    uint8_t* buff = wrawByteBuffer();

    if(_reverseDigitDir)
        buff[ ( numMod() * DigitPerModule - 1 ) - ( segNum * numMod() + modNum ) ] = value;
    else
        buff[                                     ( segNum * numMod() + modNum ) ] = value;
}


void MAX72XXSevenSegmentDriver::clearAllDigits()
{
    // Check if it is not yet initialized
    if( !rawByteBuffer() ) return;

    // Fill the buffer with zeroes
    memset( wrawByteBuffer(), 0, numMod() * DigitPerModule );
}


} // namespace eCxx
