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


#ifndef ECXX_PLATFORM_CHARACTER_LCD_H
#define ECXX_PLATFORM_CHARACTER_LCD_H


#include <eCxx/CTL/BasicOutputWriter.h>

#include <eCxx/Platform/Common/Application.h>


namespace eCxx {


//
// A simple character LCD driver base class
//
// WARNING : This driver only supports interfacing with character LCD in 4-bit mode.
//
// WARNING : All functions derived from BasicOutputWriter<T> that accept parameters with data types
//           'PGMCharPointer' and 'PGMCharPointerFar' expect the pointers to come from program
//           memory exclusively!
//
// NOTE    : Whenever possible, use the versions that accept near program pointers because those that
//           accept the far program pointers may have more overhead and restrictions.
//
template <typename OutputMethodT>
class CharacterLCD : public BasicOutputWriter< CharacterLCD<OutputMethodT> > {
    public:
        enum class CursorMode : uint8_t {
            None, // Default
            Blink,
            NoBlink
        };

        enum class EntryMode : uint8_t {
            CursorMoveRight, // Default
            CursorMoveLeft,
            DisplayShiftLeft,
            DisplayShiftRight
        };

    public:
        inline CharacterLCD();
        inline ~CharacterLCD();

        inline void begin(uint8_t numCol, uint8_t numRow);
        inline void end();

        inline void selectTopHalf() noexcept; // NOTE : Only for an LCD that actually consist of two stacked LCDs
        inline void selectBotHalf() noexcept; // NOTE : Only for an LCD that actually consist of two stacked LCDs

        inline void clear() const noexcept;

        inline void cursorHome() const noexcept;
        inline void setCursorPosition(uint8_t col, uint8_t row) const noexcept;

        inline void moveCursorLeft() const noexcept;
        inline void moveCursorRight() const noexcept;

        inline void scrollDisplayLeft() const noexcept;
        inline void scrollDisplayRight() const noexcept;

        inline void setDisplay(bool displayOn, CursorMode cursorMode) const noexcept;
        inline void setEntryMode(EntryMode entryMode) const noexcept;

        template <typename PGMPointerT, typename PGMReadByteFunctorT>
        inline void _setUserDefinedCharacter(uint8_t pos, PGMPointerT data8) const noexcept;

        template <typename PGMReadByteFunctorT = PGMReadByte> inline void setUserDefinedCharacter(uint8_t pos, PGMCharPointer    data8) const noexcept;
        template <typename PGMReadByteFunctorT = PGMReadByte> inline void setUserDefinedCharacter(uint8_t pos, PGMCharPointerFar data8) const noexcept;

        inline bool write(uint8_t value) const noexcept;

    private:
        uint8_t _numCol;
        uint8_t _numRow;

        bool    _twoUnit;
};


//
// Include the implementation file
//
#define INCLUDE_ECXX_PLATFORM_CHARACTER_LCD_TPP
#include "CharacterLCD.tpp"


} // namespace eCxx


#endif
