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


#include <eCxx/Platform/CharacterLCD_TWI.h>


namespace eCxx {


//
// A simple character LCD driver using TWI output method
//
void CharacterLCD_TWI::setBacklight(bool on, bool immediate)
{
    // Do not proceed if there is no actual slave device found
    if(!_slaveFound) return;

    // Store the backlight state
    _blState = ( (on && _blActvHigh) || (!on && !_blActvHigh) )
             ? _pinBL
             : 0;

    // Set it immediately as needed
    if(immediate) {
        // Do not use pulseData() here because it will assert the EN pin

        TWIMaster& twi = Application::twiMaster();

        twi.beginTransmission(_twiAddress);
        twi.write            (_blState   );
        twi.endTransmission  (           );
    }
}


void CharacterLCD_TWI::sendLNibble(uint8_t value) const noexcept
{
    // Do not proceed if there is no actual slave device found
    if(!_slaveFound) return;

    // Send the low nibble only
    const uint8_t l = value & 0x0F;

    uint8_t composite = 0;

    if(l & 0b00001000) composite |= _pinD7;
    if(l & 0b00000100) composite |= _pinD6;
    if(l & 0b00000010) composite |= _pinD5;
    if(l & 0b00000001) composite |= _pinD4;

    pulseData(composite);
}


void CharacterLCD_TWI::sendCommand(uint8_t value) const noexcept
{
    // Write   -> RW = 0
    // Command -> RS = 0

    // Do not proceed if there is no actual slave device found
    if(!_slaveFound) return;

    // Send the high nibble
    const uint8_t h = value >> 4;

    uint8_t composite = _blState;

    if(h & 0b00001000) composite |= _pinD7;
    if(h & 0b00000100) composite |= _pinD6;
    if(h & 0b00000010) composite |= _pinD5;
    if(h & 0b00000001) composite |= _pinD4;

    pulseData(composite);

    // Send the low nibble
    const uint8_t l = value & 0x0F;

    composite = _blState;

    if(l & 0b00001000) composite |= _pinD7;
    if(l & 0b00000100) composite |= _pinD6;
    if(l & 0b00000010) composite |= _pinD5;
    if(l & 0b00000001) composite |= _pinD4;

    pulseData(composite);
}


void CharacterLCD_TWI::sendData(uint8_t value) const noexcept
{
    // Write -> RW = 0
    // Data  -> RS = 1

    // Do not proceed if there is no actual slave device found
    if(!_slaveFound) return;

    // Send the high nibble
    const uint8_t h = value >> 4;

    uint8_t composite = _blState | _pinRS;

    if(h & 0b00001000) composite |= _pinD7;
    if(h & 0b00000100) composite |= _pinD6;
    if(h & 0b00000010) composite |= _pinD5;
    if(h & 0b00000001) composite |= _pinD4;

    pulseData(composite);

    // Send the low nibble
    const uint8_t l = value & 0x0F;

    composite = _blState | _pinRS;

    if(l & 0b00001000) composite |= _pinD7;
    if(l & 0b00000100) composite |= _pinD6;
    if(l & 0b00000010) composite |= _pinD5;
    if(l & 0b00000001) composite |= _pinD4;

    pulseData(composite);
}


//
// Private member functions
//
void CharacterLCD_TWI::init()
{
    // Do not proceed if it has already been initialized (a slave device has been found)
    if(_slaveFound) return;

    // Initialize the TWI
    TWIMaster& twi = Application::twiMaster();

    // Check if the slave does exist
    twi.beginTransmission(_twiAddress);
    _slaveFound = ( twi.endTransmission() == TWIMaster::TWIResultCode::NoError );

    if(!_slaveFound) return;

    // Discard unneeded reply from the slave
    uint8_t discard;
    twi.read(discard);

    // Set all port to low
    twi.beginTransmission(_twiAddress);
    twi.write            (0          );
    twi.endTransmission  (           );
}


void CharacterLCD_TWI::uninit()
{
    // Do not proceed if it has never been initialized before (a slave device has never been found)
    if(_slaveFound) return;

    // Turn off backlight
    setBacklight(false, true);

    // Reset flag
    _slaveFound = false;
}


void CharacterLCD_TWI::pulseData(uint8_t composite) const noexcept
{
    TWIMaster& twi = Application::twiMaster();

                   twi.beginTransmission(_twiAddress       );
    if(_useRWAsE2) twi.write            (_pinRW | composite);
    else           twi.write            (_pinEN | composite);
                   twi.endTransmission  (                  );

                   twi.beginTransmission(_twiAddress       );
                   twi.write            (         composite);
                   twi.endTransmission  (                  );
}


} // namespace eCxx
