#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 port_list {
    template<const uintptr_t... PORTS>
      struct list {
        static constexpr const uint8_t count = sizeof...(PORTS);
        template<const uintptr_t OFF>
          struct offset {
            static constexpr volatile uint8_t*const value[] = {
              ((uint8_t*const)(PORTS+OFF))...
            };
          };
      };
  }


#elif GENERIC_IO <= 1

  struct Ports {
    static_assert(
         port_list::Ports::count == port_list::DDRs::count
      && port_list::Ports::count == port_list::Pins::count,
      "There must be as many DDRs as PORTs as PINs"
    );
    static constexpr volatile uint8_t*const out[] = port_list::Ports::offset<io_offset>::value;
    static constexpr volatile uint8_t*const in[]  = port_list::Pins::offset<io_offset>::value;
    static constexpr volatile uint8_t*const io[]  = port_list::DDRs::offset<io_offset>::value;
    static constexpr const uint8_t count = port_list::Ports::count;
  };

  template<const int PORT>
    struct Port {
      typedef Port<PORT> _port_;
      static constexpr volatile uint8_t& out = *(Ports::out[PORT]);
      static constexpr volatile uint8_t& in  = *(Ports::in[PORT]);
      static constexpr volatile uint8_t& io  = *(Ports::io[PORT]);
      inline Port& operator=(const uint8_t mask){
        out = mask;
        return *this;
      }
      inline operator int(){
        return in;
      }
      inline operator volatile uint8_t&(){
        return out;
      }
      template<const int PIN>
        struct Pin {
          typedef _port_ Port;
          static struct pin_t {
            BIT_OPERATIONS(pin_t,Port::out,PIN)
            inline operator bool(){
              return Port::out & (1<<PIN);
            }
          } out;
          static struct io_t {
            BIT_OPERATIONS(io_t,Port::io,PIN)
            inline operator bool(){
              return Port::io & (1<<PIN);
            }
          } io;
          static struct in_t {
            inline operator bool(){
              return Port::in & (1<<PIN);
            }
          } in;
          BIT_OPERATIONS(Pin,out,PIN)
          inline operator bool(){
            return in;
          }
          static inline void on(){
            out = true;
          }
          static inline void off(){
            out = false;
          }
          static inline void toggle(){
            out ^= true;
          }
          static inline void setIn(){
            io = false;
          }
          static inline void setOut(){
            io = true;
          }
          inline operator int(){
            return 1<<PIN;
          }
        };
    };

#undef BIT_OPERATIONS

  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

