#include "gpio.hpp"

#include <fstream>
#include <sstream>
#include <stdexcept>
#include <string>

using namespace std;

static string construct_filename(const string &prefix, int pin, const string &suffix)
{
	stringstream filename;
	filename << prefix << pin << suffix;
	return filename.str();
}

gpio::gpio(int pin, pin_direction direction, pin_type type, bool default_state)
	: pin(pin),
	  direction(direction),
	  type(type),
	  current_state(false)
{
	ofstream export_file("/sys/class/gpio/export");
	export_file << pin;
	export_file.close();
	ofstream direction_file(construct_filename("/sys/class/gpio/gpio", pin, "/direction").c_str());
	if (direction == pin_direction::INPUT)
	{
		direction_file << "in";
	}
	else
	{
		direction_file << "out";
	}
	direction_file.close();
	if (direction == pin_direction::OUTPUT)
	{
		set_value(default_state, false);
	}
}

gpio::gpio(gpio&& old)
	: pin(old.pin),
	  direction(old.direction),
	  type(old.type),
	  current_state(old.current_state)
{
	old.pin = -1;
}

gpio::~gpio()
{
	if (pin != -1)
	{
		ofstream unexport_file("/sys/class/gpio/unexport");
		unexport_file << pin;
	}
}

void gpio::set_value(bool on, bool use_cache)
{
	if (pin == -1)
		throw logic_error("Usage of moved gpio");
	if (direction == pin_direction::INPUT)
	{
		stringstream errormsg;
		errormsg << "Cannot write to input pin " << pin;
		throw logic_error(errormsg.str());
	}
	if (!use_cache || current_state != on)
	{
		bool value;
		if (type == pin_type::HIGH_ON)
			value = on;
		else
			value = !on;
		ofstream value_file(construct_filename("/sys/class/gpio/gpio", pin, "/value"));
		value_file << value;
	}
	current_state = on;
}

void gpio::set_value(bool on)
{
	set_value(on, true);
}

bool gpio::get_value()
{
	if (pin == -1)
		throw logic_error("Usage of moved gpio");
	if (direction == pin_direction::OUTPUT)
		return current_state;
	ifstream value_file(construct_filename("/sys/class/gpio/gpio", pin, "/value"));
	bool value;
	value_file >> value;
	bool on;
	if (type == pin_type::HIGH_ON)
		on = value;
	else
		on = !value;
	return on;
}

int gpio::get_pin() const
{
	return pin;
}

