/* $Id$ */

#include <cstring>
#include <cassert>
#include <string>
#include <cerrno>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "throw.h"
#include "comm.h"

// xxx

CComm::CComm(): m_fd(-1)
{
}

void CComm::Init(const char *dev)
{
	m_fd = open(dev, O_RDWR | O_NOCTTY | O_SYNC);
	if (m_fd == -1)
		Throw("Cannot open dev %s: %s", dev, strerror(errno));

	struct termios t;
	if (tcgetattr(m_fd, &t) == -1)
		Throw("Cannot get attributes: %s", strerror(errno));

	t.c_iflag = 0;
	t.c_oflag = 0;
	t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
	t.c_cflag &= ~(CSIZE | PARENB);
	t.c_cflag |= CS8;

	t.c_cc[VMIN]  = 1;
	t.c_cc[VTIME] = 0;

	if (cfsetispeed(&t, B9600) == -1 || cfsetospeed(&t, B9600) == -1)
		Throw("Cannot set speed: %s", strerror(errno));

	if (tcsetattr(m_fd, TCSAFLUSH, &t) == -1)
		Throw("Cannot set attributes: %s", strerror(errno));
}

CComm::~CComm()
{
	if (m_fd != -1)
	{
		tcdrain(m_fd);
		close(m_fd);
	}
}

void CComm::Send(const void *data, size_t len)
{
	const char *p((const char *) data);
	size_t rem(len);

	while (rem)
	{
		ssize_t rs(write(m_fd, p, rem /* 1 */));
		// tcdrain(m_fd);
		// usleep(500000);
		if (rs == -1)
		{
			if (errno == EAGAIN || errno == EINTR)
				continue;

			Throw("Cannot write to device: %s", strerror(errno));
		}

		if (!rs || (size_t) rs > rem)
			Throw("Internal inconsistency (%u %u)", (size_t) rs, rem);

		p += (size_t) rs;
		rem -= (size_t) rs;
	}

	if (tcdrain(m_fd) == -1)
		Throw("Cannot drain device: %s", strerror(errno));
}

void CComm::Recv(void *data, size_t len)
{
	// xxx timeout

	char *p((char *) data);
	size_t rem(len);

	while (rem)
	{
		ssize_t rs(read(m_fd, p, rem));
		if (!rs)
			Throw("Cannot read from device: EOF");

		if (rs == -1)
		{
			if (errno == EAGAIN || errno == EINTR)
				continue;

			Throw("Cannot read from device: %s", strerror(errno));
		}

		if ((size_t) rs > rem)
			Throw("Internal inconsistency (%u %u)", (size_t) rs, rem);

		p += (size_t) rs;
		rem -= (size_t) rs;
	}
}
