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

#define DEFAULT_BUFFER_LENGTH (1024)


/*
 *
 *   close
 *
 */

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


/*
 *
 *  multiplexing and buffering routines
 *
 */

void my::multiplexed::pipeend::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::pipeend::should_watch_read(void) {
	return ((getmode() == my::pipeend::mode_read) && !is_closed);
};

bool my::multiplexed::pipeend::should_watch_write(void) {
	return (
			(getmode() == my::pipeend::mode_write) &&
			( ! is_closed ) &&
			( ! tx_ok )
		   );
}

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


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

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

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

/*
 *
 *   assign(...)
 *
 */

/*
#include <iostream>
using namespace std;
*/

void my::multiplexed::pipeend::assign(int f, my::pipeend::mode m) {
	try {
		//cerr << "my::multiplexed::pipeend::assign() entry." << endl;
		my::pipeend::assign(f,m);
	} catch (...) {
		throw;
	}

	is_closed = false;
	tx_ok     = false;
	//cerr << "my::multiplexed::pipeend::assign() exit." << endl;
}


/* end: muxpipeend.cc */
