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


#ifndef INCLUDE_ECXX_PLATFORM_SPECIFIC_APPLICATION_H
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_PLATFORM_SPECIFIC_APPLICATION_H


#ifndef ECXX_PLATFORM_DUMMY_APPLICATION_H
#define ECXX_PLATFORM_DUMMY_APPLICATION_H


#include <eCxx/Platform/Common/SPI.h>
#include <eCxx/Platform/Common/TWIMaster.h>
#include <eCxx/Platform/Common/TWISlave.h>
#include <eCxx/Platform/Common/UART.h>


namespace eCxx {


//
// A dummy application
//
class Application : public Application_Base, public Application_CRTPBase<Application> {
    public:
        // Forward declaration to the application's configuration structure
        struct Config;

        // Explicitly pull these from the Application_Base class
        using Application_Base::PinMode;

        using Application_Base::InvalidPin;

        using Application_Base::UARTBufferSize;
        using Application_Base::TWIBufferSize;
        using Application_Base::BSRBufferSize;

        using Application_Base::CommunicationTimeoutCycles;
        using Application_Base::MaxScheduledUserCallback;

        using Application_Base::VoidFuncPtr;
        using Application_Base::VoidFuncPtrArg;

        // Declare the OWIMaster class
        using OWIMaster = Application_OWIMaster<Application>;

        // Minimum and maximum ADC pin numbers
        static constexpr uint8_t ADCPinMin = InvalidPin;
        static constexpr uint8_t ADCPinMax = InvalidPin;

        // Interrupt mode
        enum class InterruptMode : uint8_t { Rising, Falling, Change };

    public:
        static void halt() noexcept __ECXX_ATTR_NORETURN;
        static void softReset() noexcept __ECXX_ATTR_NORETURN;

        static void getSoftUARTBitDelayUS(uint16_t& txBit, uint16_t& rxBit, uint16_t& rxCtr, uint32_t baudrate) noexcept;

        static void setPinMode(uint8_t pin, PinMode mode);
        static void setActivityIndicatorLED(bool on);

        static void digitalWrite(uint8_t pin, bool bit);
        static bool digitalRead(uint8_t pin);

        static void analogWrite(uint8_t pin, uint8_t val);
        static uint16_t analogRead(uint8_t pin);

        static bool nvStorageFormat();
        static bool nvStorageWrite(const void* src, uint16_t size, uint16_t offset = 0);
        static bool nvStorageRead (      void* dst, uint16_t size, uint16_t offset = 0);

        static uint32_t micros() noexcept;
        static uint32_t millis() noexcept;

        static inline void delayUS(uint16_t uS) noexcept;
        static inline void delayMS(uint16_t mS) noexcept;

        static UART& uart();

        static SPI& spi();

        static TWIMaster& twiMaster();
        static TWISlave& twiSlave();

        static OWIMaster& owiMaster();

        static inline void yield(bool /*immediate*/ = false) noexcept;
        static constexpr uint32_t getCPUFrequency() noexcept;

        static bool attachInterruptArg(uint8_t pin, VoidFuncPtrArg userFunc, void* arg, InterruptMode mode);
        static bool detachInterrupt(uint8_t pin);

        static void initialize(const Application::Config& config);
        static uint32_t processEvent(bool blinkActivityIndicatorLED = true);

    public:
        // Fast-digital-read wrapper class
        class _PinFDR {
            public:
                _PinFDR() noexcept;
                bool operator()() const noexcept;
        };

        // Fast-digital-write wrapper class
        class _PinFDW {
            public:
                _PinFDW() noexcept;
                void operator()(bool bit) const noexcept;
        };

        // Fast-digital-read-and-write wrapper class
        class _PinFDX {
            public:
                _PinFDX() noexcept;
                void setUsePullUp(bool usePullUp);
                void setReadMode() const noexcept;
                void setWriteMode(bool initialBit) const noexcept;
                bool read() const noexcept;
                void write(bool bit) const noexcept;
        };

        // Fast-digital-read/write initialization functions
        static void _preparePinFDW(_PinFDW& pinFDW, uint8_t pin) noexcept;
        static void _preparePinFDR(_PinFDR& pinFDR, uint8_t pin) noexcept;
        static void _preparePinFDX(_PinFDX& pinFDX, uint8_t pin) noexcept;

    private:
        // OWIMaster low-level functions
        class OWIData;

        static bool owiBegin(OWIData& owiData, uint8_t pin);
        static void owiEnd(OWIData& owiData);

        static bool owiReset(const OWIData& owiData);

        static void owiWriteBit0(const OWIData& owiData);
        static void owiWriteBit1(const OWIData& owiData);
        static bool owiReadBit(const OWIData& owiData);

        static void __delay_loop4(uint16_t cnt) noexcept;
        static void __delay_loop5(uint16_t cnt) noexcept;

        friend class OneWireSoftUART;
};


inline void Application::delayUS(uint16_t) noexcept
{}


inline void Application::delayMS(uint16_t) noexcept
{}


inline void Application::yield(bool) noexcept
{}


constexpr uint32_t Application::getCPUFrequency() noexcept
{ return 100000000UL; }


} // namespace eCxx


#endif
