#ifndef IO_INSIDE
#error "Don't include this file directly, use <io> instant"
#endif

#ifndef GENERIC_IO
#include <stdint.h>
#endif

#if !defined(GENERIC_IO) || GENERIC_IO < 2
#ifndef GENERIC_IO
#define GENERIC_IO 0
#endif

namespace uc {

#if GENERIC_IO <= 0

  namespace ports_list {
    template<const uintptr_t... PORTS>
      struct PORTs {
        template<const uintptr_t... DDRS>
          struct DDRs {
            static_assert(
              (sizeof...(PORTS)) == (sizeof...(DDRS)),
              "There must be as many DDRs as PORTs"
            );
            static constexpr const int count = sizeof...(PORTS);
            static constexpr volatile uint8_t*const value[] = {
              ((uint8_t*const)PORTS)...
            };
            static constexpr volatile uint8_t*const io[] = {
              ((uint8_t*const)DDRS)...
            };
          };
      };
  }

#elif GENERIC_IO <= 1

  template<const int PORT>
    struct Port {
      static constexpr volatile uint8_t& value = *(Ports::value[PORT]);
      static constexpr volatile uint8_t& DDR   = *(Ports::io[PORT]);
      template<const int PIN>
        struct Pin {
          static struct pin_t {
            inline pin_t& operator=(const bool p){
              if(p)
                Port<PORT>::value |= 1<<PIN;
              else
                Port<PORT>::value &= 1<<PIN;
              return *this;
            }
            inline pin_t& operator|=(const bool p){
              Port<PORT>::value |= p<<PIN;
              return *this;
            }
            inline pin_t& operator ^= (const bool p){
              Port<PORT>::value ^= p<<PIN;
              return *this;
            }
            inline pin_t& operator&=(const bool p){
              Port<PORT>::value &= p<<PIN;
              return *this;
            }
            inline operator bool(){
              return Port<PORT>::value & (1<<PIN);
            }
          } value;
          static struct ddr_t {
            inline ddr_t& operator=(const bool p){
              if(p)
                Port<PORT>::DDR &= 1<<PIN;
              else
                Port<PORT>::DDR |= 1<<PIN;
              return *this;
            }
            inline ddr_t& operator|=(const bool p){
              Port<PORT>::DDR |= p<<PIN;
              return *this;
            }
            inline ddr_t& operator&=(const bool p){
              Port<PORT>::DDR &= p<<PIN;
              return *this;
            }
            inline operator bool() const {
              return Port<PORT>::io & (1<<PIN);
            }
          } io;
          static inline void on(){
            Pin::value = true;
          }
          static inline void off(){
            Pin::value = false;
          }
          static inline void toggle(){
            Pin::value ^= true;
          }
          static inline void in(){
            Pin::io = true;
          }
          static inline void out(){
            Pin::io = false;
          }
        };
    };

  template<uintptr_t PORT,uintptr_t PIN>
    using Pin = typename Port<PORT>::template Pin<PIN>;

#endif

}

#endif

#define GENERIC_IO_TMP 1
#undef GENERIC_IO
#define GENERIC_IO GENERIC_IO_TMP + 1
#undef GENERIC_IO_TMP

