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


#include <eCxx/CTL/Types.h>

#include "PinName.h"


#ifndef ECXX_PLATFORM_AVRX_LOGIC_H
#define ECXX_PLATFORM_AVRX_LOGIC_H


#if defined(__AVR_ATmega808__) || defined(__AVR_ATmega1608__) || defined(__AVR_ATmega3208__) || defined(__AVR_ATmega4808__) || \
    defined(__AVR_ATmega809__) || defined(__AVR_ATmega1609__) || defined(__AVR_ATmega3209__) || defined(__AVR_ATmega4809__)


namespace eCxx {


//
// A simple AVR CCL wrapper
//
class Logic : public Uncopyable {
    public:
        inline ~Logic() = default;

    private:
        Logic();

        friend class Application;

    public:
        // LUT input selection
        enum class Input : uint8_t {
            // These values are derived from the datasheet
            Disabled    = 0x00, // Masked (tied low)
            Feedback    = 0x01, // Feedback from the sequential logic output of LUT[N}
            Link        = 0x02, // Link from output of LUT[N + 1]
            EventA      = 0x03, // Event source A
            EventB      = 0x04, // Event source B
            Pin         = 0x05, // Input pin of LUT[N]
            PinPullUp   = 0x85, // Input pin of LUT[N] with pull-up
            AC0_Output  = 0x06, // AC0  output
            UART_TXD    = 0x08, //      UART0.TXD UART1.TXD UART2.TXD
            SPI0_Output = 0x09, // SPI0 MOSI      MOSI      SCK
            TCA0_Output = 0x0A, // TCA0 WO0       WO1       WO2
            TCB_Output  = 0x0C  //      TCB0.WO   TCB1.WO   TCB2.WO
        };

        // LUT output selection
        enum class Output : uint8_t {
            // These values are derived from the datasheet
            Disabled  = 0x00, // Disabled
            Default   = 0x01, // Default   output pin
            Alternate = 0x02  // Alternate output pin
        };

        // Clock-source selection
        enum class ClockSource : uint8_t {
            // These values are from the datasheet
            Peripheral  = 0x00, // Peripheral clock
            In2         = 0x01, // LUT[N] input 2
            InternalHF  = 0x04, // Internal 20/16 MHz oscillator
            Internal32K = 0x05, // Internal 32768  Hz oscillator
            Internal1K  = 0x06  // Internal  1024  Hz oscillator
        };

        // Filter selection
        enum class Filter : uint8_t {
            // These values are from the datasheet
            Disabled     = 0x00, // Disabled
            Synchronizer = 0x01, // Synchronizer enabled
            Filter       = 0x02  // Filter       enabled
        };

        // Edge-detector selection
        enum class EdgeDetector : uint8_t {
            // These values are from the datasheet
            Disabled = 0x00, // Disabled
            Enabled  = 0x01  // Enabled
        };

        // Sequencer selection
        enum class Sequencer : uint8_t {
            // These values are from the datasheet
            Disabled   = 0x00, // Disabled
            DFlipFlop  = 0x01, // D  flip-flop
            JKFlipFlop = 0x02, // JK flip-flop
            DLatch     = 0x03, // D  latch
            RSLatch    = 0x04  // RS latch
        };

        // LUT configuration
        class LUT {
            public:
                static constexpr uint8_t NumberOfInput = 3;

                bool         enabled;
                Input        input[NumberOfInput];
                Output       output;
                ClockSource  clockSource;
                Filter       filter;
                EdgeDetector edgeDetector;
                uint8_t      truthTable;

                void reset();

            private:
                LUT(uint8_t lutNumber);
                void reset_impl();

                const uint8_t _lutNumber;

                friend class Logic;
        };

        static constexpr uint8_t NumberOfLUT       = 4;
        static constexpr uint8_t NumberOfSequencer = 2;

    public:
        // Configurable public properties
        LUT       lut      [NumberOfLUT      ];
        Sequencer sequencer[NumberOfSequencer];

    public:
        bool begin();
        bool end();

        void reset();

        void attachInterrupt(uint8_t lut, eCxx::VoidCallback userFunc, Application::InterruptMode mode);
        void detachInterrupt(uint8_t lut);
};


} // namespace eCxx


//
// Unsupported MCU
//
#else

#error "unsupported AVR MCU"

#endif


#endif
