/*
 *
 *
 *    muxtty.cc
 *
 *    C++ source code for class my::multiplexed::tty 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++/muxtty.h>
#include "bufcpy.h"

#define DEFAULT_BUFFER_LENGTH (1024)


/*
 *
 *   open/close
 *
 */

void my::multiplexed::tty::open (const std::string &fn, int flgs) {
	my::tty::open (fn, flgs);
	is_closed = false;
}

void my::multiplexed::tty::close (void) throw() {
	is_closed = true;
	my::tty::close();
}


/*
 *
 *  multiplexing and buffering routines
 *
 */

void my::multiplexed::tty::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::tty::should_watch_write(void) {
	return (( ! is_closed ) && ( ! tx_ok ));
}

bool my::multiplexed::tty::read_action (void) {
	ssize_t rtn = -1;
	
	do {
		try {
			rtn = my::tty::get (c_buffer, buffer_len);
		} catch (const tty::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::tty::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::tty::put(c_buffer, len);
			} catch (const tty::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;
}


/*
 *
 *   I/O routines
 *
 */

/*
size_t my::multiplexed::tty::get (unsigned char *b, size_t len) {
	return get (static_cast<void *>(b), len);
}

size_t my::multiplexed::tty::get (char *b, size_t len) {
	return get (static_cast<void *>(b), len);
}

size_t my::multiplexed::tty::get (void *buf, size_t len) {
	unsigned char *b = static_cast<unsigned char *>(buf);
	size_t rtn = rx_buffer.size();
	if (len < rtn) rtn = len;
	if (rtn > 0) {
		for (unsigned char *cp = b; cp != (b + rtn); ++cp) {
			*cp = rx_buffer.front();
			rx_buffer.pop();
		}
	}

	return rtn;
}

size_t my::multiplexed::tty::put (const unsigned char *b, size_t len) {
	return put (static_cast<const void *>(b), len);
}

size_t my::multiplexed::tty::put (const char *b, size_t len) {
	return put (static_cast<const void *>(b), len);
}

size_t my::multiplexed::tty::put (const void *buf, size_t len) {
	const unsigned char *b = static_cast<const unsigned char *>(buf);

	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::tty::put(b, len);
			if (rtn != len) {
				for (const unsigned char *cp = b + rtn; cp != (b + len); ++cp) {
					tx_buffer.push(*cp);
				}
			}
		} else {
			for (const unsigned char *cp = b; cp != (b + len); ++cp) {
				tx_buffer.push(*cp);
			}
		}
	} else {
		return 0;
	}

	return len;
}
*/

size_t my::multiplexed::tty::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::tty::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::tty::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::tty::get (std::string &s) {
	size_t rtn = rx_buffer.size();
	bufcpy(s, rx_buffer);
	return rtn;
}           

size_t my::multiplexed::tty::put (const std::string &s) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::tty::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::tty::put (const unsigned char *buf, size_t len) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::tty::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::tty::put (const char *buf, size_t len) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::tty::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::tty::put (const void *buf, size_t len) {
	if (!is_closed) {
		if (tx_ok) {
			tx_ok = false;
			size_t rtn = my::tty::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;
}



/*
 *
 *   ctors and dtors
 *
 */

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

my::multiplexed::tty::tty (const std::string &fn, int flgs) : my::tty(fn,flgs), buffer_len(0), c_buffer(0), is_closed(true), tx_ok(false) {
	buffer(DEFAULT_BUFFER_LENGTH);
	is_closed = false;
}

my::multiplexed::tty::~tty (void) throw () {
	if (c_buffer != 0)
		delete [] c_buffer;
}

/* end: muxtty.cc */
