//-----------------------------------------------------------------------------
// AScript codec.japanese module
//-----------------------------------------------------------------------------
#include "Module_codecs_japanese.h"

AScript_BeginModule(codecs_japanese)

//-----------------------------------------------------------------------------
// CP932
//-----------------------------------------------------------------------------
Codec::Result Codec_Encoder_CP932::FeedUTF32(unsigned long codeUTF32, char &chConv)
{
	unsigned short codeCP932 = UTF16ToCP932(static_cast<unsigned short>(codeUTF32));
	if (codeCP932 == 0x0000) {
		chConv = '\0';
		return RESULT_Error;
	} else if ((codeCP932 & ~0xff) == 0) {
		char ch = static_cast<char>(codeCP932 & 0xff);
		if (IsProcessEOL() && ch == '\n') {
			StoreChar('\n');
			chConv = '\r';
		} else {
			chConv = ch;
		}
	} else {
		StoreChar(static_cast<char>(codeCP932 & 0xff));
		chConv = static_cast<char>(codeCP932 >> 8);
	}
	return RESULT_Complete;
}

Codec::Result Codec_Decoder_CP932::FeedChar(char ch, char &chConv)
{
	unsigned long codeUTF32 = 0x00000000;
	if (_codeCP932 == 0x0000) {
		if (IsSJISFirst(ch)) {
			_codeCP932 = static_cast<unsigned char>(ch);
			return RESULT_None;
		}
		codeUTF32 = CP932ToUTF16(static_cast<unsigned char>(ch));
	} else {
		_codeCP932 = (_codeCP932 << 8) | static_cast<unsigned char>(ch);
		codeUTF32 = CP932ToUTF16(_codeCP932);
		_codeCP932 = 0x0000;
	}
	if (IsProcessEOL() && codeUTF32 == '\r') return RESULT_None;
	return FeedUTF32(codeUTF32, chConv);
}

//-----------------------------------------------------------------------------
// EUCJP
//-----------------------------------------------------------------------------
Codec::Result Codec_Encoder_EUCJP::FeedUTF32(unsigned long codeUTF32, char &chConv)
{
	unsigned short codeCP932 = UTF16ToCP932(static_cast<unsigned short>(codeUTF32));
	unsigned short codeEUCJP = CP932ToEUCJP(codeCP932);
	if (codeEUCJP == 0x0000) {
		chConv = '\0';
		return RESULT_Error;
	} else if ((codeEUCJP & ~0xff) == 0) {
		char ch = static_cast<char>(codeEUCJP & 0xff);
		if (IsProcessEOL() && ch == '\n') {
			StoreChar('\n');
			chConv = '\r';
		} else {
			chConv = ch;
		}
	} else {
		StoreChar(static_cast<char>(codeEUCJP & 0xff));
		chConv = static_cast<char>(codeEUCJP >> 8);
	}
	return RESULT_Complete;
}

Codec::Result Codec_Decoder_EUCJP::FeedChar(char ch, char &chConv)
{
	unsigned long codeUTF32 = 0x00000000;
	if (_codeEUCJP == 0x0000) {
		if (ch & 0x80) {
			_codeEUCJP = static_cast<unsigned char>(ch);
			return RESULT_None;
		}
		codeUTF32 = CP932ToUTF16(static_cast<unsigned char>(ch));
	} else {
		_codeEUCJP = (_codeEUCJP << 8) | static_cast<unsigned char>(ch);
		unsigned short codeCP932 = EUCJPToCP932(_codeEUCJP);
		codeUTF32 = CP932ToUTF16(codeCP932);
		_codeEUCJP = 0x0000;
	}
	if (IsProcessEOL() && codeUTF32 == '\r') return RESULT_None;
	return FeedUTF32(codeUTF32, chConv);
}

typedef Codec_Encoder_CP932 Codec_Encoder_Shift_JIS;
typedef Codec_Decoder_CP932 Codec_Decoder_Shift_JIS;
typedef Codec_Encoder_CP932 Codec_Encoder_MS_Kanji;
typedef Codec_Decoder_CP932 Codec_Decoder_MS_Kanji;

AScript_ImplementCodecFactory(EUCJP, "euc-jp")
AScript_ImplementCodecFactory(CP932, "cp932")
AScript_ImplementCodecFactory(Shift_JIS, "shift_jis")
AScript_ImplementCodecFactory(MS_Kanji, "ms_kanji")

//-----------------------------------------------------------------------------
// JIS
//-----------------------------------------------------------------------------
Codec::Result Codec_Encoder_JIS::FeedUTF32(unsigned long codeUTF32, char &chConv)
{
	unsigned short codeCP932 = UTF16ToCP932(static_cast<unsigned short>(codeUTF32));
	if (codeCP932 < 0x80) {
		char ch = static_cast<char>(codeCP932 & 0xff);
		if (_mode == MODE_ASCII) {
			if (IsProcessEOL() && ch == '\n') {
				StoreChar('\n');
				chConv = '\r';
			} else {
				chConv = ch;
			}
		} else {
			if (IsProcessEOL() && ch == '\n') {
				StoreChar('\n');
				StoreChar('\r');
			} else {
				StoreChar(ch);
			}
			StoreChar('B');
			StoreChar('(');
			chConv = 0x1b;
			_mode = MODE_ASCII;
		}
	} else if (0xa0 < codeCP932 && codeCP932 < 0xe0) {
		if (_mode != MODE_JISKANA) {
			StoreChar('I');
			StoreChar('(');
			chConv = 0x1b;
			_mode = MODE_JISKANA;
		}
		chConv = static_cast<unsigned char>(codeCP932 - 0x80);
	} else if (codeCP932 < 0x100) {
		return RESULT_Error;
	} else {
		unsigned short codeJIS = CP932ToJIS(codeCP932);
		if (codeJIS == 0x0000) {
			chConv = '\0';
			return RESULT_Error;
		}
		if (_mode == MODE_JISC) {
			StoreChar(static_cast<char>(codeJIS & 0xff));
			chConv = static_cast<char>(codeJIS >> 8);
		} else {
			StoreChar(static_cast<char>(codeJIS & 0xff));
			StoreChar(static_cast<char>(codeJIS >> 8));
			StoreChar('@');
			StoreChar('$');
			chConv = 0x1b;
			_mode = MODE_JISC;
		}
	}
	return RESULT_Complete;
}

Codec::Result Codec_Decoder_JIS::FeedChar(char ch, char &chConv)
{
	if (_stat == STAT_Start) {
		if (ch == 0x1b) {
			_codeJIS = 0x0000;
			_stat = STAT_Escape;
			return RESULT_None;
		}
	} else if (_stat == STAT_Escape) {
		if (ch == '$') {
			_stat = STAT_Dollar;
		} else if (ch == '&') {
			_stat = STAT_Ampersand;
		} else if (ch == '(') {
			_stat = STAT_LParenthesis;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	} else if (_stat == STAT_Dollar) {
		if (ch == '@') {
			_mode = MODE_JISC;
			_stat = STAT_Start;
		} else if (ch == 'B') {
			_mode = MODE_JISX1983;
			_stat = STAT_Start;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	} else if (_stat == STAT_Ampersand) {
		if (ch == '@') {
			_stat = STAT_JISX1990;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	} else if (_stat == STAT_LParenthesis) {
		if (ch == 'B') {
			_mode = MODE_ASCII;
			_stat = STAT_Start;
		} else if (ch == 'J') {
			_mode = MODE_JISROMA;
			_stat = STAT_Start;
		} else if (ch == 'I') {
			_mode = MODE_JISKANA;
			_stat = STAT_Start;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	} else if (_stat == STAT_JISX1990) {
		if (ch == 0x1b) {
			_stat = STAT_Escape2nd;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	} else if (_stat == STAT_Escape2nd) {
		if (ch == '$') {
			_stat = STAT_Dollar2nd;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	} else if (_stat == STAT_Dollar2nd) {
		if (ch == 'B') {
			_mode = MODE_JISX1990;
			_stat = STAT_Start;
		} else {
			return RESULT_Error;
		}
		return RESULT_None;
	}
	unsigned short codeCP932 = 0x0000;
	if (_mode == MODE_JISC || _mode == MODE_JISX1983 || _mode == MODE_JISX1990) {
		if (_codeJIS == 0x0000) {
			_codeJIS = ch;
			return RESULT_None;
		}
		_codeJIS = (_codeJIS << 8) | ch;
		codeCP932 = JISToCP932(_codeJIS);
	} else if (_mode == MODE_ASCII) {
		codeCP932 = ch;
	} else if (_mode == MODE_JISROMA) {
		codeCP932 = ch;	// incorrect process
	} else if (_mode == MODE_JISKANA) {
		if (0x20 < ch && ch < 0x60) {
			codeCP932 = static_cast<unsigned char>(ch) + 0x80;
		} else {
			return RESULT_Error;
		}
	}
	if (IsProcessEOL() && codeCP932 == '\r') return RESULT_None;
	unsigned long codeUTF32 = CP932ToUTF16(codeCP932);
	_codeJIS = 0x0000;
	return FeedUTF32(codeUTF32, chConv);
}

typedef Codec_Encoder_JIS Codec_Encoder_ISO2022JP;
typedef Codec_Decoder_JIS Codec_Decoder_ISO2022JP;

AScript_ImplementCodecFactory(JIS, "jis")
AScript_ImplementCodecFactory(ISO2022JP, "iso-2022-jp")

//-----------------------------------------------------------------------------
// AScript module functions: japanese
//-----------------------------------------------------------------------------
// Module entry
AScript_ModuleEntry()
{
	AScript_RegisterCodecFactory(EUCJP);
	AScript_RegisterCodecFactory(CP932);
	AScript_RegisterCodecFactory(Shift_JIS);
	AScript_RegisterCodecFactory(MS_Kanji);
	AScript_RegisterCodecFactory(JIS);
	AScript_RegisterCodecFactory(ISO2022JP);
}

AScript_ModuleTerminate()
{
}

AScript_EndModule(codecs_japanese, japanese)

AScript_RegisterModule(codecs_japanese)
