#ifndef __PIN_H__
#define __PIN_H__

#include "types.h"
#include "gpio.h"
#include "pin_names.h"

template <PinNames pin>
class Out
{
	public:

		Out() {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			port->ConfigurePin((pin & 0xF), GPIO::OUTPUT, GPIO::TOTEM_POLE, GPIO::FLOATING,
					            GPIO::LOW_SPEED, GPIO::SYSTEM);
		}

		Out(GPIO::PinDriver driver, GPIO::PinState state, GPIO::PinSpeed speed) {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			port->ConfigurePin((pin & 0xF), GPIO::OUTPUT, driver, state, speed, GPIO::SYSTEM);
		}

		void Write(int value) {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			if (value) {
				port->Set(pin & 0xF);
			} else {
				port->Clear(pin & 0xF);
			}
		}
		int Read() const {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			return port->Test(pin & 0xF);
		}
		Out& operator= (int value) { Write(value); return *this; };
		Out& operator= (Out& rhs) { return Write(rhs.Read()); };
		operator int() const { return Read(); };
};

template <PinNames pin>
class In
{
	public:
		In() {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			port->ConfigurePin((pin & 0xF), GPIO::INPUT, GPIO::TOTEM_POLE, GPIO::FLOATING,
					            GPIO::LOW_SPEED, GPIO::SYSTEM);
		}

		In(GPIO::PinState state) {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			port->ConfigurePin((pin & 0xF), GPIO::INPUT, GPIO::TOTEM_POLE, state, GPIO::LOW_SPEED, GPIO::SYSTEM);
		}

		int Read() const {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			return port->Get(pin & 0xF);
		}

		operator int() const { return Read(); };
};

template <PinNames pin>
class AlternateFunction
{
	public:
		AlternateFunction(GPIO::PinAlternateFunction af) {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			port->ConfigurePin((pin & 0xF), GPIO::ALTERNATE_FUNCTION, GPIO::TOTEM_POLE,
					            GPIO::FLOATING, GPIO::LOW_SPEED, af);
		}

		AlternateFunction(GPIO::PinAlternateFunction af, GPIO::PinDriver driver, GPIO::PinState state, GPIO::PinSpeed speed) {
			GPIO* port = reinterpret_cast<GPIO *> (GPIOA_BASE + (pin >> 4) * sizeof(GPIO));
			port->ConfigurePin((pin & 0xF), GPIO::ALTERNATE_FUNCTION, driver, state, speed, af);
		}
};


#endif // __PIN_H__
