/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "glk/unicode.h"
#include "glk/unicode_gen.h"
#include "common/textconsole.h"

namespace Glk {

size_t strlen_uni(const uint32 *s) {
	size_t len = 0;
	while (*s++)
		++len;
	return len;
}

uint bufferChangeCase(uint32 *buf, uint len, uint numchars, BufferChangeCase destcase,
						BufferChangeCond cond, int changerest) {
	uint ix, jx;
	uint32 *outbuf;
	uint32 *newoutbuf;
	uint outcount;
	int dest_block_rest = 0, dest_block_first = 0;
	int dest_spec_rest = 0, dest_spec_first = 0;

	switch (cond) {
	case COND_ALL:
		dest_spec_rest = destcase;
		dest_spec_first = destcase;
		break;
	case COND_LINESTART:
		if (changerest)
			dest_spec_rest = CASE_LOWER;
		else
			dest_spec_rest = CASE_IDENT;
		dest_spec_first = destcase;
		break;
	}

	dest_block_rest = dest_spec_rest;
	if (dest_block_rest == CASE_TITLE)
		dest_block_rest = CASE_UPPER;
	dest_block_first = dest_spec_first;
	if (dest_block_first == CASE_TITLE)
		dest_block_first = CASE_UPPER;

	newoutbuf = nullptr;
	outcount = 0;
	outbuf = buf;

	for (ix = 0; ix < numchars; ix++) {
		int target;
		int isfirst;
		uint res;
		uint *special;
		uint *ptr;
		uint speccount;
		uint32 ch = buf[ix];

		isfirst = (ix == 0);

		target = (isfirst ? dest_block_first : dest_block_rest);

		if (target == CASE_IDENT) {
			res = ch;
		} else {
			gli_case_block_t *block;

			GET_CASE_BLOCK(ch, &block);
			if (!block)
				res = ch;
			else
				res = block[ch & 0xFF][target];
		}

		if (res != 0xFFFFFFFF || res == ch) {
			// simple case
			if (outcount < len)
				outbuf[outcount] = res;
			outcount++;
			continue;
		}

		target = (isfirst ? dest_spec_first : dest_spec_rest);

		// complicated cases
		GET_CASE_SPECIAL(ch, &special);
		if (!special) {
			warning("inconsistency in cgunigen.c");
			continue;
		}
		ptr = &unigen_special_array[special[target]];
		speccount = *(ptr++);

		if (speccount == 1) {
			// simple after all
			if (outcount < len)
				outbuf[outcount] = ptr[0];
			outcount++;
			continue;
		}

		// Now we have to allocate a new buffer, if we haven't already.
		if (!newoutbuf) {
			newoutbuf = new uint32[len + 1];
			if (!newoutbuf)
				return 0;
			if (outcount)
				memcpy(newoutbuf, buf, outcount * sizeof(uint32));
			outbuf = newoutbuf;
		}

		for (jx = 0; jx < speccount; jx++) {
			if (outcount < len)
				outbuf[outcount] = ptr[jx];
			outcount++;
		}
	}

	if (newoutbuf) {
		uint finallen = outcount;
		if (finallen > len)
			finallen = len;
		if (finallen)
			memcpy(buf, newoutbuf, finallen * sizeof(uint));
		free(newoutbuf);
	}

	return outcount;
}

} // End of namespace Glk
