/*
 *
 *
 *    muxunixsocket.cc
 *
 *    C++ source code for class my::multiplexed::unixsocket member functions
 *    and/or static member data.
 *
 *    =========================================================================
 *
 *    Copyright (C) 1999-2003 by Matt Roberts, KK5JY.
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    =========================================================================
 *
 *
 */


#include <my++/muxunixsocket.h>
#include "bufcpy.h"

#define DEFAULT_BUFFER_LENGTH (1024)

my::multiplexed::unixsocket::unixsocket (void)
	: buffer_len(0), c_buffer(0), is_closed(true),
	is_inbound(false), is_listening(false), tx_ok(false) {
	buffer(DEFAULT_BUFFER_LENGTH);
}

my::multiplexed::unixsocket::unixsocket (const address &a)
	: my::unixsocket(a), buffer_len(0), c_buffer(0), is_closed(false),
	is_inbound(false), is_listening(true), tx_ok(false) {
	buffer(DEFAULT_BUFFER_LENGTH);
	is_closed = false;
}

my::multiplexed::unixsocket::unixsocket (const std::string &a)
	: my::unixsocket(a), buffer_len(0), c_buffer(0), is_closed(false),
	is_inbound(false), is_listening(true), tx_ok(false) {
	buffer(DEFAULT_BUFFER_LENGTH);
	is_closed = false;
}

void my::multiplexed::unixsocket::buffer (size_t len) {
	if (is_closed) {
		if (len > 0) {
			if (c_buffer != 0)
				delete [] c_buffer;
			buffer_len = len;
			c_buffer = new unsigned char [buffer_len];
		}
	}
}


bool my::multiplexed::unixsocket::should_watch_read(void) {
	return (is_listening || (!is_closed));
}

bool my::multiplexed::unixsocket::should_watch_write(void) {
	if (is_listening)
		return false;
	else
		return ( ! tx_ok );
}

bool my::multiplexed::unixsocket::read_action (void) {
	if (is_listening) {
		is_inbound = true;
		return true;
	} else {
		ssize_t rtn = -1;
		do {
			try {
				rtn = my::unixsocket::get (c_buffer, buffer_len);
			} catch (const unixsocket::exception &e) {
				if (e.errnum() != EINTR)
					throw;
				rtn = -1;
			}
		} while (rtn < 0);

		if (rtn > 0)
			bufcpy (rx_buffer, c_buffer, rtn);
		else
			is_closed = true;
	}

	return true;
}

bool my::multiplexed::unixsocket::write_action (void) {
	if (tx_buffer.size() > 0) {
		tx_ok = false;
		size_t len = tx_buffer.size();
		ssize_t rtn = -1;
		if (len > buffer_len)
			len = buffer_len;
		bufcpy (c_buffer, tx_buffer, len);
		do {
			try {
				rtn = my::unixsocket::put(c_buffer, len);
			} catch (const unixsocket::exception &e) {
				if (e.errnum() != EINTR)
					throw;
				rtn = -1;
			}
		} while (rtn < 0);
	} else {
		tx_ok = true;
	}

	// no need to bother the application, we took care of it...
	return false;
}

void my::multiplexed::unixsocket::listen (int backlog) {
	is_listening = true;
	my::unixsocket::listen (backlog);
}

void my::multiplexed::unixsocket::listen (void) {
	is_listening = true;
	my::unixsocket::listen();
}

void my::multiplexed::unixsocket::accept (socket &s, std::string &a) {
	my::multiplexed::unixsocket &s2 = dynamic_cast<my::multiplexed::unixsocket&>(s);
	is_inbound = false;
	my::unixsocket::accept(s2, a);
	s2.is_closed = false;
	s2.is_listening = false;
}

void my::multiplexed::unixsocket::accept (socket &s, socket::address &a) {
	my::multiplexed::unixsocket &s2 = dynamic_cast<my::multiplexed::unixsocket&>(s);
	is_inbound = false;
	my::unixsocket::accept(s2, a);
	s2.is_closed = false;
	s2.is_listening = false;
}

void my::multiplexed::unixsocket::accept (socket &s) {
	my::multiplexed::unixsocket &s2 = dynamic_cast<my::multiplexed::unixsocket&>(s);
	is_inbound = false;
	my::unixsocket::accept(s2);
	s2.is_closed = false;
	s2.is_listening = false;
}

void my::multiplexed::unixsocket::connect (const socket::address &a) {
	my::unixsocket::connect (a);
	is_closed = false;
	is_listening = false;
}

void my::multiplexed::unixsocket::connect (const std::string &a) {
	my::unixsocket::connect (a);
	is_closed = false;
	is_listening = false;
}

size_t my::multiplexed::unixsocket::get (unsigned char *buf, size_t len) {
	size_t rtn = rx_buffer.size();
	if (len < rtn)
		rtn = len;
	if (rtn > 0)
		bufcpy (buf, rx_buffer, rtn);
	return rtn;
}

size_t my::multiplexed::unixsocket::get (char *buf, size_t len) {
	size_t rtn = rx_buffer.size();
	if (len < rtn)
		rtn = len;
	if (rtn > 0)
		bufcpy ((unsigned char*)buf, rx_buffer, rtn);
	return rtn;
}

size_t my::multiplexed::unixsocket::get (void *buf, size_t len) {
	size_t rtn = rx_buffer.size();
	if (len < rtn)
		rtn = len;
	if (rtn > 0)
		bufcpy (static_cast<unsigned char *>(buf), rx_buffer, rtn);
	return rtn;
}

size_t my::multiplexed::unixsocket::get (std::string &s) {
	size_t rtn = rx_buffer.size();
	bufcpy(s, rx_buffer);
	return rtn;
}           

size_t my::multiplexed::unixsocket::put (const std::string &s) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::unixsocket::put(s.c_str(), s.size());
			if (rtn != s.size()) {
				std::string remainder(s.begin() + rtn, s.end());
				bufcpy(tx_buffer, remainder);
			}
		} else {
			bufcpy(tx_buffer, s);
		}
	} else {
		return 0;
	}

	return s.size();
}


size_t my::multiplexed::unixsocket::put (const unsigned char *buf, size_t len) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::unixsocket::put(buf, len);
			if (rtn != len)
				bufcpy (tx_buffer, buf + rtn, len - rtn);
		} else {
			bufcpy (tx_buffer, buf, len);
		}
	} else {
		return 0;
	}

	return len;
}

size_t my::multiplexed::unixsocket::put (const char *buf, size_t len) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::unixsocket::put(buf, len);
			if (rtn != len)
				bufcpy (tx_buffer, (const unsigned char*)(buf + rtn), len - rtn);
		} else {
			bufcpy (tx_buffer, (const unsigned char*)(buf), len);
		}
	} else {
		return 0;
	}

	return len;
}

size_t my::multiplexed::unixsocket::put (const void *buf, size_t len) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::unixsocket::put(buf, len);
			if (rtn != len)
				bufcpy (tx_buffer, static_cast<const unsigned char *>(buf) + rtn, len - rtn);
		} else {
			bufcpy (tx_buffer, static_cast<const unsigned char *>(buf), len);
		}
	} else {
		return 0;
	}

	return len;
}
