/*
 *	aegis - project change supervisor
 *	Copyright (C) 1999-2001 Peter Miller;
 *	All rights reserved.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program 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 General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 *
 * MANIFEST: functions to manipulate wchar_t output through a common API
 */

#include <ac/string.h>

#include <error.h> /* for assert */
#include <mem.h>
#include <str.h>
#include <trace.h>
#include <wide_output/private.h>
#include <wstr.h>


void
wide_output_delete(fp)
	wide_output_ty	*fp;
{
	size_t		ncb;

	trace(("wide_output_delete(fp = %08lX)\n{\ntype is wide_output_%s\n",
		(long)fp, fp->vptr->typename));
	wide_output_flush(fp);

	/*
	 * run any delete callbacks specified
	 */
	ncb = fp->ncallbacks;
	fp->ncallbacks = 0;
	while (ncb > 0)
	{
		wide_output_callback_record *crp;

		--ncb;
		crp = &fp->callback[ncb];
		crp->func(fp, crp->arg);
	}
	if (fp->callback)
		mem_free(fp->callback);
	fp->ncallbacks = 0;
	fp->ncallbacks_max = 0;
	fp->callback = 0;

	/*
	 * now run the destructor
	 */
	if (fp->vptr->destructor)
		fp->vptr->destructor(fp);

	/*
	 * now get rid of it
	 */
	fp->vptr = 0;
	mem_free(fp->buffer);
	fp->buffer_size = 0;
	fp->buffer = 0;
	fp->buffer_position = 0;
	fp->buffer_end = 0;
	mem_free(fp);
	trace(("}\n"));
}


string_ty *
wide_output_filename(fp)
	wide_output_ty	*fp;
{
	string_ty	*result;

	trace(("wide_output_filename(fp = %08lX)\n{\ntype is wide_output_%s\n",
		(long)fp, fp->vptr->typename));
	assert(fp);
	assert(fp->vptr);
	assert(fp->vptr->filename);
	result = fp->vptr->filename(fp);
	trace(("return \"%s\";\n", result->str_text));
	trace(("}\n"));
	return result;
}


#ifdef wide_output_putwc
#undef wide_output_putwc
#endif

void
wide_output_putwc(fp, wc)
	wide_output_ty	*fp;
	wint_t		wc;
{
	trace(("wide_output_putwc(fp = %08lX, wc = %04lX)\n{\n\
type is wide_output_%s\n", (long)fp, (long)wc, fp->vptr->typename));
	assert(fp);
	assert(fp->buffer);
	assert(fp->buffer_size);
	assert(fp->buffer_position >= fp->buffer);
	assert(fp->buffer_end == fp->buffer + fp->buffer_size);
	assert(fp->buffer_position <= fp->buffer_end);
	if (fp->buffer_position >= fp->buffer_end)
	{
		assert(fp->vptr);
		assert(fp->vptr->write);
		fp->vptr->write(fp, fp->buffer, fp->buffer_size);
		fp->buffer_position = fp->buffer;
	}
	*(fp->buffer_position)++ = wc;
	trace(("}\n"));
}


void
wide_output_putws(fp, ws)
	wide_output_ty	*fp;
	const wchar_t	*ws;
{
	const wchar_t	*wse;

	trace(("wide_output_putws(fp = %08lX, s = %08lX)\n{\n\
type is wide_output_%s\n", (long)fp, (long)ws, fp->vptr->typename));
	assert(ws);
	for (wse = ws; *wse; ++wse)
		;
	if (wse > ws)
		fp->vptr->write(fp, ws, wse - ws);
	trace(("}\n"));
}


void
wide_output_write(fp, data, len)
	wide_output_ty	*fp;
	const wchar_t	*data;
	size_t		len;
{
	trace(("wide_output_write(fp = %08lX, data = %08lX, len = %ld)\n{\n\
type wide_output_%s\n", (long)fp, (long)data, (long)len, fp->vptr->typename));
	assert(data);
	/* assert(len); ideal, but not necessary */
	assert(fp);
	assert(fp->vptr);
	assert(fp->vptr->write);
	if (fp->buffer_position + len <= fp->buffer_end)
	{
		memcpy(fp->buffer_position, data, len * sizeof(wchar_t));
		fp->buffer_position += len;
	}
	else
	{
		size_t  nwc;
	
		nwc = fp->buffer_position - fp->buffer;
		fp->vptr->write(fp, fp->buffer, nwc);
		fp->buffer_position = fp->buffer;
	
		if (len < fp->buffer_size)
		{
			memcpy(fp->buffer, data, len * sizeof(wchar_t));
			fp->buffer_position += len;
		}
		else
			fp->vptr->write(fp, data, len);
	}
	trace(("}\n"));
}


void
wide_output_flush(fp)
	wide_output_ty	*fp;
{
	trace(("wide_output_flush(fp = %08lX)\n{\n\
type is wide_output_%s\n", (long)fp, fp->vptr->typename));
	assert(fp);
	assert(fp->vptr);
	if (fp->buffer_position > fp->buffer)
	{
		size_t		nwc;

		nwc = fp->buffer_position - fp->buffer;
		assert(fp->vptr->write);
		fp->vptr->write(fp, fp->buffer, nwc);
		fp->buffer_position = fp->buffer;
	}
	assert(fp->vptr->flush);
	fp->vptr->flush(fp);
	trace(("}\n"));
}


int
wide_output_page_width(fp)
	wide_output_ty	*fp;
{
	int		result;

	trace(("wide_output_page_width(fp = %08lX)\n{\n\
type is wide_output_%s\n", (long)fp, fp->vptr->typename));
	assert(fp);
	assert(fp->vptr);
	assert(fp->vptr->page_width);
	result = fp->vptr->page_width(fp);
	trace(("return %d;\n", result));
	trace(("}\n"));
	return result;
}


int
wide_output_page_length(fp)
	wide_output_ty	*fp;
{
	int		result;

	trace(("wide_output_page_length(fp = %08lX)\n{\n\
type is wide_output_%s\n", (long)fp, fp->vptr->typename));
	assert(fp);
	assert(fp->vptr);
	assert(fp->vptr->page_length);
	result = fp->vptr->page_length(fp);
	trace(("return %d;\n", result));
	trace(("}\n"));
	return result;
}


void
wide_output_end_of_line(fp)
	wide_output_ty	*fp;
{
	/*
	 * If possible, just stuff a newline into the buffer and bail.
	 * This results in the fewest deeper calls.
	 */
	trace(("wide_output_end_of_line(fp = %08lX)\n{\n\
type is wide_output_%s\n", (long)fp, fp->vptr->typename));
	assert(fp);
	if
	(
		fp->buffer_position > fp->buffer
	&&
		fp->buffer_position[-1] != '\n'
	&&
		fp->buffer_position < fp->buffer_end
	)
	{
		*(fp->buffer_position)++ = '\n';
		trace(("}\n"));
		return;
	}

	/*
	 * If there is something in the buffer, we need to flush it,
	 * so that the deeper eoln will have the current state.
	 */
	assert(fp->vptr);
	if (fp->buffer_position > fp->buffer)
	{
		size_t		nwc;

		nwc = fp->buffer_position - fp->buffer;
		assert(fp->vptr->write);
		fp->vptr->write(fp, fp->buffer, nwc);
		fp->buffer_position = fp->buffer;
	}

	/*
	 * Now ask the deeper class to do it's end of line thing.
	 */
	assert(fp->vptr->end_of_line);
	fp->vptr->end_of_line(fp);
	trace(("}\n"));
}


void
wide_output_delete_callback(fp, func, arg)
	wide_output_ty	*fp;
	wide_output_callback_ty func;
	void		*arg;
{
	wide_output_callback_record *crp;

	if (fp->ncallbacks >= fp->ncallbacks_max)
	{
		size_t		nbytes;

		fp->ncallbacks_max = fp->ncallbacks_max * 2 + 4;
		nbytes = fp->ncallbacks_max * sizeof(fp->callback[0]);
		fp->callback = mem_change_size(fp->callback, nbytes);
	}
	crp = &fp->callback[fp->ncallbacks++];
	crp->func = func;
	crp->arg = arg;
}


void
wide_output_put_wstr(fp, ws)
	wide_output_ty	*fp;
	wstring_ty	*ws;
{
	assert(fp);
	if (!ws || !ws->wstr_length)
		return;
	wide_output_write(fp, ws->wstr_text, ws->wstr_length);
}


void
wide_output_put_str(fp, s)
	wide_output_ty	*fp;
	string_ty	*s;
{
	wstring_ty	*ws;

	if (!s || !s->str_length)
		return;
	ws = wstr_n_from_c(s->str_text, s->str_length);
	wide_output_put_wstr(fp, ws);
	wstr_free(ws);
}


void
wide_output_put_cstr(fp, s)
	wide_output_ty	*fp;
	const char	*s;
{
	wstring_ty	*ws;

	if (!s || !*s)
		return;
	ws = wstr_from_c(s);
	wide_output_put_wstr(fp, ws);
	wstr_free(ws);
}