/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	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.

**********************************************************************/


#include	<stdlib.h>
#include	"memory_debug.h"
#include	"long_char.h"

typedef struct ucs4_work {
	unsigned char	buf[4];
	char		co[4];
	char		len;
	char		out_start;
} UCS4_WORK;

void * open_ucs4();
int close_ucs4(unsigned char *,void *);
int ucs42int(L_CHAR *,void*,unsigned char);
int int2ucs4(unsigned char *,void*,L_CHAR);

CODE_METHOD ucs4_cm = {
	"UCS-4",
	LCC_UCS4,
	0,
	8,
	0,
	{0,0},
	open_ucs4,
	close_ucs4,
	ucs42int,
	int2ucs4,
	0
};


void *
open_ucs4()
{
UCS4_WORK * ret;
int i;
	ret = d_alloc(sizeof(UCS4_WORK),99);
	ret->len = 0;
	for ( i = 0 ; i < 4 ; i ++ ) {
		ret->buf[i] = 0;
		ret->co[i] = -1;
	}
	ret->out_start = 0;
	return ret;
}

int
close_ucs4(unsigned char * ret,void * work)
{
	d_f_ree(work);
	return 0;
}

int
ucs42int(L_CHAR * ret,void * w,unsigned char chr)
{
UCS4_WORK * _w;
int i;
unsigned int b;
L_CHAR ch;
	_w = w;
	_w->buf[_w->len++] = chr;
	if ( _w->len == 4 ) {
		if ( _w->co[0] == -1 ) {
			b = 0;
			for ( i = 0 ; i < 4 ; i ++ ) {
				b |= (((int)_w->buf[i])&0x00ff)<<(24-8*i);
			}
		endian:
			if ( b == 0x0000feff ) {
				_w->co[0] = 24;
				_w->co[1] = 16;
				_w->co[2] = 8;
				_w->co[3] = 0;
			}
			else if ( b == 0x0000fffe ) {
				_w->co[0] = 16;
				_w->co[1] = 24;
				_w->co[2] = 0;
				_w->co[3] = 8;
			}
			else if ( b == 0xfeff0000 ) {
				_w->co[0] = 8;
				_w->co[1] = 0;
				_w->co[2] = 24;
				_w->co[3] = 16;
			}
			else if ( b == 0xfffe0000 ) {
				_w->co[0] = 0;
				_w->co[1] = 8;
				_w->co[2] = 16;
				_w->co[3] = 24;
			}
			else {
				_w->co[0] = 24;
				_w->co[1] = 16;
				_w->co[2] = 8;
				_w->co[3] = 0;
				goto code;
			}
			_w->len = 0;
			return 0;
		}
		else {
		code:
			ch = 0;
			b = 0;
			for ( i = 0 ; i < 4 ; i ++ ) {
				ch |= _w->buf[i]<<_w->co[i];
				b |= _w->buf[i]<<(24-8*i);
			}
			if ( b == 0x0000feff ||
					b == 0x0000fffe ||
					b == 0xfeff0000 ||
					b == 0xfffe0000 )
				goto endian;
			*ret = ch;
			_w->len = 0;
			return 1;
		}
	}
	return 0;
}

int
int2ucs4(unsigned char * ret,void * _work,L_CHAR ch)
{
UCS4_WORK * w;
L_CHAR * r;
	w = _work;
	if ( (ch&LCZM_4BYTE) != LCZ_4BYTE )
		return 0;
	r = (L_CHAR*)ret;
	if ( w->out_start ) {
		*r = ch;
		return 4;
	}
	else {
		w->out_start = 1;
		r[0] = 0x0000feff;
		r[1] = ch;
		return 8;
	}
}
