#include "gserial.h"
#include "piprocess.h"
#include <pigpio.h>
#include <piiostream.h>
#ifndef WINDOWS
//#  define USE_TCDRAIN // disable to use piUSleep() instead of tcdrain()
#endif
#ifdef USE_TCDRAIN
#  include <termios.h>
#endif


PIGSerial::PIGSerial(): PISerial() {
	gpio_num = gpio_read_value = gpio_write_value = 0;
	CONNECTU(&timer_wq, tickEvent, this, tickWrite);
}


PIGSerial::~PIGSerial() {
}


ssize_t PIGSerial::writeDevice(const void * data, ssize_t max_size) {
	mutex_q.lock();
	write_q.enqueue(PIByteArray(data, max_size));
	mutex_q.unlock();
	return max_size;
}


void PIGSerial::_write(const PIByteArray & ba) {
	//piCoutObj << "send " << ba.size_s();
#ifndef USE_TCDRAIN
	int uss = ba.size_s() * (9 + (isParameterSet(TwoStopBits) ? 2 : 1)) * 1000000 / int(outSpeed());
#endif
	if (gpio_num > 0)
		PIGPIO::instance()->pinSet(gpio_num, gpio_write_value);
	PISerial::writeDevice(ba.data(), ba.size_s());
#ifdef USE_TCDRAIN
	tcdrain(PISerial::fd);
#else
	piUSleep(uss);
#endif
	if (gpio_num > 0)
		PIGPIO::instance()->pinSet(gpio_num, gpio_read_value);
}


void PIGSerial::tickWrite() {
	mutex_q.lock();
	if (write_q.isEmpty()) {
		mutex_q.unlock();
		return;
	}
	PIByteArray ba = write_q.dequeue();
	mutex_q.unlock();
	_write(ba);
}


bool PIGSerial::openDevice() {
	if (!timer_wq.isRunning())
		timer_wq.start(50);
	return PISerial::openDevice();
}


bool PIGSerial::closeDevice() {
	if (timer_wq.isRunning())
		timer_wq.stop();
	mutex_q.unlock();
	return PISerial::closeDevice();
}


PIString PIGSerial::constructFullPathDevice() const {
	PIString ret;
	PIIOTextStream ts(&ret);
	ts << gpio_num << ":" << gpio_read_value << ":" << gpio_write_value << ":";
	ts << path() << ":" << int(inSpeed()) << ":" << dataBitsCount();
	if (parameters()[ParityControl]) {
		if (parameters()[ParityOdd]) ts << ":O";
		else ts << ":E";
	} else ts << ":N";
	if (parameters()[TwoStopBits]) ts << ":2";
	else ts << ":1";
	return ret;
}


void PIGSerial::configureFromFullPathDevice(const PIString & full_path) {
	PIStringList pl = full_path.split(":");
	for (int i = 0; i < pl.size_s(); ++i) {
		PIString p(pl[i]);
		switch (i) {
		case 0: gpio_num = p.toInt(); break;
		case 1: gpio_read_value = p.toInt(); break;
		case 2: gpio_write_value = p.toInt(); break;
		case 3: setPath(p); break;
		case 4: setSpeed((Speed)(p.toInt())); break;
		case 5: setDataBitsCount(p.toInt()); break;
		case 6:
			p = p.toLowerCase();
			if (p != "n") setParameter(ParityControl);
			if (p == "o") setParameter(ParityOdd);
			break;
		case 7: if (p.toInt() == 2) setParameter(TwoStopBits); break;
		}
	}
	if (gpio_num > 0) {
		PIGPIO::instance()->initPin(gpio_num, PIGPIO::Out);
		PIGPIO::instance()->pinSet(gpio_num, gpio_read_value);
	}
}
