/*
 * 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_UART.h>


namespace eCxx {


//
// A simple character LCD driver using UART output method
//
void CharacterLCD_UART::setBacklight(bool on, bool immediate)
{
    // Do not proceed if it has not been initialized
    if(!_initialized) 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

        if(_pinUARTEN != Application::InvalidPin)
            Application::digitalWrite( _pinUARTEN, _atsUARTEN != ActiveState::Low);

        _uart->write(_blState);

        if(_pinUARTEN != Application::InvalidPin)
            Application::digitalWrite( _pinUARTEN, _atsUARTEN == ActiveState::Low);
    }
}


void CharacterLCD_UART::sendLNibble(uint8_t value) const noexcept
{
    // Do not proceed if it has not been initialized
    if(!_initialized) 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_UART::sendCommand(uint8_t value) const noexcept
{
    // Write   -> RW = 0
    // Command -> RS = 0

    // Do not proceed if it has not been initialized
    if(!_initialized) 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_UART::sendData(uint8_t value) const noexcept
{
    // Write -> RW = 0
    // Data  -> RS = 1

    // Do not proceed if it has not been initialized
    if(!_initialized) 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_UART::init()
{
    // Do not proceed if it has already been initialized
    if(_initialized) return;

    // Set flag
    _initialized = true;

    // Set all port to low
    _uart->write(0);
}


void CharacterLCD_UART::uninit()
{
    // Do not proceed if it has never been initialized before
    if(!_initialized) return;

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

    // Reset flag
    _initialized = false;
}


void CharacterLCD_UART::pulseData(uint8_t composite) const noexcept
{
    if(_pinUARTEN != Application::InvalidPin)
        Application::digitalWrite( _pinUARTEN, _atsUARTEN != ActiveState::Low);

    if(_useRWAsE2) { _uart->write(_pinRW | composite); _uart->flush(); }
    else           { _uart->write(_pinEN | composite); _uart->flush(); }

                     _uart->write(         composite); _uart->flush();

    if(_pinUARTEN != Application::InvalidPin)
        Application::digitalWrite( _pinUARTEN, _atsUARTEN == ActiveState::Low);
}


} // namespace eCxx
