/* stateless 8bit encoding support => no support for CP1255, 1258 or TCVN
 * functions in this file have an identical API to the encoding functions
 * in UnicodeLib. see unicode/encoding.h for documentation. */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "unicode/charsets.h"
#include "unicode/encoding.h"

#include "internal.h"

struct table_entry {
	const int enc_num;
	const char *alias;
	const char *filename;
};

/* Table should be ordered by enc_num */
static const struct table_entry mapping_table[] = {
	{ csASCII, "/US-ASCII/ANSI_X3.4-1968/ISO-IR-6/ANSI_X3.4-1986/ISO_646.IRV:1991/ASCII/ISO646-US/US/IBM367/CP367/CSASCII/", 0 },
	{ csHPRoman8, "/HP-ROMAN8/ROMAN8/R8/CSHPROMAN8/", "HPR8" },
	{ csMacintosh, "/MACINTOSH/MAC/MACROMAN/MAC-ROMAN/CSMACINTOSH/X-MAC-ROMAN/", "Apple.Roman"},
	{ csPC8CodePage437, "/IBM437/CP437/437/CSPC8CODEPAGE437/", "Microsoft.CP437" },
	{ csPC775Baltic, "/IBM775/CP775/CSPC775BALTIC/", "Microsoft.CP775" },
	{ csPC850Multilingual, "/IBM850/CP850/850/CSPC850MULTILINGUAL/", "Microsoft.CP850" },
	{ csPCp852, "/IBM852/CP852/852/CSPCP852/", "Microsoft.CP852" },
	{ csIBM855, "/IBM855/CP855/855/CSIMB855/", "Microsoft.CP855" },
	{ csIBM857, "/IBM857/CP857/857/CSIBM857/", "Microsoft.CP857" },
	{ csIBM860, "/IBM860/CP860/860/CSIBM860/", "Microsoft.CP860" },
	{ csIBM861, "/IBM861/CP861/861/CP-IS/CSIBM861/", "Microsoft.CP861" },
	{ csPC862LatinHebrew, "/IBM862/CP862/862/CSPC862LATINHEBREW", "Microsoft.CP862" },
	{ csIBM863, "/IBM863/CP863/863/CSIBM863/", "Microsoft.CP863" },
	{ csIBM864, "/IBM864/CP864/CSIBM864/", "Microsoft.CP864" },
	{ csIBM865, "/IBM865/CP865/865/CSIBM865/", "Microsoft.CP865" },
	{ csIBM866, "/IBM866/CP866/866/CSIBM866/", "Microsoft.CP866" },
	{ csIBM869, "/IBM869/CP869/869/CP-GR/CSIBM869/", "Microsoft.CP869" },
	{ csKOI8R, "/KOI8-R/CSKOI8R/", "KOI8-R" },
	{ 2088, "/KOI8-U/", "KOI8-U" },
	{ 2089, "/IBM00858/CP858/CCSID00858/CP00858/PC-MULTILINGUAL-850+EURO/", "Microsoft.CP858" },
	{ csWindows1250, "/CP1250/WINDOWS-1250/MS-EE/", "Microsoft.CP1250" },
	{ csWindows1251, "/CP1251/WINDOWS-1251/MS-CYRL/", "Microsoft.CP1251" },
	{ csWindows1252, "/CP1252/WINDOWS-1252/MS-ANSI/", "Microsoft.CP1252" },
	{ csWindows1253, "/CP1253/WINDOWS-1253/MS-GREEK/", "Microsoft.CP1253" },
	{ csWindows1254, "/CP1254/WINDOWS-1254/MS-TURK/", "Microsoft.CP1254" },
	{ csWindows1256, "/CP1256/WINDOWS-1256/MS-ARAB/", "Microsoft.CP1256" },
	{ csWindows1257, "/CP1257/WINDOWS-1257/WINBALTRIM/", "Microsoft.CP1257" },
	/* values below here are arbitrary */
	{ 3000, "/CP737/", "Microsoft.CP737" },
	{ 3001, "/CP853/", "Microsoft.CP853" },
	{ 3002, "/CP856/", "Microsoft.CP856" },
	{ 3004, "/CP874/WINDOWS-874/", "Microsoft.CP874" },
	{ 3005, "/CP922/", "Microsoft.CP922" },
	{ 3006, "/CP1046/", "Microsoft.CP1046" },
	{ 3007, "/CP1124/", "Microsoft.CP1124" },
	{ 3008, "/CP1125/WINDOWS-1125/", "Microsoft.CP1125" },
	{ 3009, "/CP1129/", "Microsoft.CP1129" },
	{ 3010, "/CP1133/IBM-CP1133/", "Microsoft.CP1133" },
	{ 3011, "/CP1161/IBM-1161/IBM1161/CSIBM1161/", "Microsoft.CP1161" },
	{ 3012, "/CP1162/IBM-1162/IBM1162/CSIBM1162/", "Microsoft.CP1162" },
	{ 3013, "/CP1163/IBM-1163/IBM1163/CSIBM1163/", "Microsoft.CP1163" },
	{ 3014, "/GEORGIAN-ACADEMY/", "GeorgA" },
	{ 3015, "/GEORGIAN-PS/", "GeorgPS" },
	{ 3016, "/KOI8-RU/", "KOI8-RU" },
	{ 3017, "/KOI8-T/", "KOI8-T" },
	{ 3018, "/X-MAC-ARABIC/MAC-ARABIC/MACARABIC/", "Apple.Arabic" },
	{ 3019, "/X-MAC-CROATIAN/MAC-CROATIAN/MACCROATIAN/", "Apple.Croatian" },
	{ 3020, "/X-MAC-GREEK/MAC-GREEK/MACGREEK/", "Apple.Greek" },
	{ 3021, "/X-MAC-HEBREW/MAC-HEBREW/MACHEBREW/", "Apple.Hebrew" },
	{ 3022, "/X-MAC-ICELAND/MAC-ICELAND/MACICELAND/", "Apple.Iceland" },
	{ 3023, "/X-MAC-ROMANIA/MAC-ROMANIA/MACROMANIA/", "Apple.Romania" },
	{ 3024, "/X-MAC-THAI/MAC-THAI/MACTHAI/", "Apple.Thai" },
	{ 3025, "/X-MAC-TURKISH/MAC-TURKISH/MACTURKISH/", "Apple.Turkish" },
	{ 3026, "/MULELAO-1/", "Mulelao" },
	{ csMacCyrillic, "/X-MAC-CYRILLIC/MAC-CYRILLIC/MACCYRILLIC/", "Apple.Cyrillic" },
	{ csMacUkrainian, "/X-MAC-UKRAINIAN/MAC-UKRAINIAN/MACUKRAINE/", "Apple.Ukrainian" },
	{ csMacCentEuro, "/MACCENTRALEUROPE/X-MAC-CENTRALEURROMAN/MAC-CENTRALEURROMAN/", "Apple.CentEuro" },
};

#define TABLE_SIZE (sizeof(mapping_table) / sizeof(mapping_table[0]))

/**
 * Look up an encoding number, based on its name
 *
 * \param name  The encoding name
 * \return The encoding number, or 0 if not found
 */
int iconv_eightbit_number_from_name(const char *name)
{
	int i;
	char buf[256];

	snprintf(buf, sizeof buf, "/%s/", name);

	/* convert to upper case */
	for (i = 0; i != strlen(buf); i++) {
		if (buf[i] >= 'a' && buf[i] <= 'z')
			buf[i] = buf[i] - 32;
	}

	LOG(("searching for: %s", buf));

	/* probably want to do a binary search here */
	for (i = 0; i != TABLE_SIZE; i++)
		if (strstr(mapping_table[i].alias, buf) != NULL) {
			LOG(("found: %d", mapping_table[i].enc_num));
			return mapping_table[i].enc_num | (1<<30);
		}

	return 0;
}

/**
 * Look up an encoding name, based on its MIB number
 *
 * \param number  The encoding number
 * \return Pointer to encoding name, or NULL if not found
 */
const char *iconv_eightbit_name_from_number(int number)
{
	int i;

	for (i = 0; i != TABLE_SIZE; i++)
		if (mapping_table[i].enc_num == number)
			return mapping_table[i].alias;

	return NULL;
}

/**
 * Read an 8bit encoded string
 *
 * \param e  The encoding context
 * \param callback  Callback function to handle generated UCS characters
 * \param s  The input string
 * \param n  The length (in bytes) of the input
 * \param handle  Callback private data pointer
 * \return The number of characters processed
 */
unsigned iconv_eightbit_read(struct encoding_context *e,
		int (*callback)(void *handle, UCS4 c), const char *s,
		unsigned int n, void *handle)
{
	UCS4 c;
	unsigned int pos;

	for (pos = 0; pos != n; pos++) {

		c = s[pos];

		LOG(("read: %d (%d)", c, pos));

		if (c < 0x80) {
			/* ASCII */
			if (callback(handle, c))
				break;
		}
		else if (c < 0x100 && e->intab) {
			LOG(("maps to: %x", e->intab[c - 0x80]));
			/* Look up in mapping table */
			if (e->intab[c - 0x80] != 0xffff) {
				if (callback(handle, e->intab[c - 0x80]))
					break;
			}
			else {
				/* character not defined in this encoding */
				return pos;
			}
		}
	}

	return pos;
}

/**
 * Write a UCS character in an 8bit encoding
 *
 * \param e  The encoding context
 * \param c  The UCS4 character
 * \param buf  Indirect pointer to output buffer
 * \param bufsize  Pointer to size of output buffer
 * \return 1 on success, 0 otherwise.
 */
int iconv_eightbit_write(struct encoding_context *e, UCS4 c,
		char **buf, int *bufsize)
{
	int i;

	/* sanity check input */
	if (!bufsize || !buf || !*buf)
		return 0;

	/* buffer full */
	if (--*bufsize < 0)
		return 0;

	if (c < 0x0080)
		/* ASCII */
		*(*buf)++ = (char)c;
	else {
		/* Perform reverse table lookup */
		for (i = 0; i != 0x80; i++) {
			if (e->outtab && e->outtab[i] == c) {
				*(*buf)++ = (char)(i+0x80);
				break;
			}
		}
	}

	LOG(("written: %d", *(*buf-1)));

	return 1;
}

/**
 * Load an 8bit encoding
 *
 * \param enc_num  The encoding number to load
 * \return Pointer to lookup table for encoding, or NULL on error
 */
unsigned short *iconv_eightbit_new(int enc_num)
{
	char filename[64];
	FILE *fp;
	unsigned int len;
	int i;
	unsigned short *ret;

	/* Lookup filename in table */
	for (i = 0; i != TABLE_SIZE; i++)
		if (mapping_table[i].enc_num == enc_num) {
			if (mapping_table[i].filename == 0)
				return NULL;
			snprintf(filename, sizeof filename,
				"Unicode:Encodings.%s",
				mapping_table[i].filename);
			break;
		}

	LOG(("opening: %s", filename));

	/* Open */
	fp = fopen(filename, "rb");
	if (!fp) {
		return NULL;
	}

	/* Get extent */
	fseek(fp, 0, SEEK_END);
	len = (unsigned int)ftell(fp);
	fseek(fp, 0, SEEK_SET);

	/* Unexpected length => give up */
	if (len != 256) {
		fclose(fp);
		return NULL;
	}

	/* Create buffer */
	ret = calloc(128, sizeof(short));
	if (!ret) {
		fclose(fp);
		return NULL;
	}

	fread(ret, 128, sizeof(short), fp);

	fclose(fp);

	return ret;
}

/**
 * Delete any 8bit encodings used by a context
 *
 * \param e  The encoding context
 */
void iconv_eightbit_delete(struct encoding_context *e)
{
	if (e->intab)
		free(e->intab);
	if (e->outtab)
		free(e->outtab);
}
