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

/*
 * ----------------------------------------------------------------------------------
 * The code in this file is based on:
 *     core_esp8266_wiring_digital.cpp - wiring digital implementation for esp8266
 * ----------------------------------------------------------------------------------
 * Copyright (C) 2015 Hristo Gochkov. All rights reserved.
 *
 * This file is part of the esp8266 core for Arduino environment.
 *
 * This library is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation; either version 2.1 of the License, or (at your option) any later
 * version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with this library; if not, write to the Free Software Foundation, Inc., 51
 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 * ----------------------------------------------------------------------------------
 */


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


#ifndef ECXX_PLATFORM_NODEMCU_PIN_DDX_H
#define ECXX_PLATFORM_NODEMCU_PIN_DDX_H


#include "GPIOUtil.h"


namespace eCxx {


//
// A simple direct digital configure, read, and write wrapper class
//
template <uint8_t PinN>
class PinDDX : public Uncopyable {
    static_assert(
        NodeMCU::userGPIOPin<true>(PinN),
        "template argument substituting PinN is out of range or does not support direct digital configure, read, and write"
    );

    public:
        inline PinDDX() = default;
        inline ~PinDDX() = default;

    public:
        inline void pinModeOutput() const __ECXX_ATTR_ALWAYS_INLINE
        {
            NodeMCU::_GPF(PinN) = GPFFS( GPFFS_GPIO(PinN) );                // Set mode to GPIO
            NodeMCU::_GPC(PinN) = ( NodeMCU::_GPC(PinN) & (0x0F << GPCI) ); // SOURCE(GPIO) | DRIVER(NORMAL) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
            GPES                = _BV(PinN);                                // Enable data
        }

        inline void digitalWrite(bool bit) const __ECXX_ATTR_ALWAYS_INLINE
        {
            if(bit) GPOS = _BV(PinN);
            else    GPOC = _BV(PinN);
        }

    public:
        inline void pinModeInput() const __ECXX_ATTR_ALWAYS_INLINE
        {
            NodeMCU::_GPF(PinN) = GPFFS( GPFFS_GPIO(PinN) );                             // Set mode to GPIO
            NodeMCU::_GPC(PinN) = ( NodeMCU::_GPC(PinN) & (0x0F << GPCI) ) | _BV(GPCD ); // SOURCE(GPIO) | DRIVER(OPEN_DRAIN) | INT_TYPE(UNCHANGED) | WAKEUP_ENABLE(DISABLED)
            GPEC                = _BV(PinN);                                             // Disable data
        }

        inline void pinModeInput(bool withPullUp) const __ECXX_ATTR_ALWAYS_INLINE
        {
            pinModeInput();

            if(withPullUp) NodeMCU::_GPF(PinN) |= _BV(GPFPU); // Enable pullup
        }

        inline bool digitalRead() const __ECXX_ATTR_ALWAYS_INLINE
        {
            return GPIP(PinN);
        }
};


} // namespace eCxx


#endif
