module charactercode;

class CharacterCode {
public:

	/*  0: ASCII */
	/*  1: JIS */
	/*  2: SJIS */
	/*  3: EUC */
	/*  4: Unknown (SJIS/EUC) */
	/*  -1: Binary */
	enum charcode { BINARY=-1, ASCII, JIS, SJIS, EUC, UNKNOWN };

	/// 文字コードを取得する
	static int getCodeFast(char[] str) {
	    int esc = 0; /* 最終バイトの状態 */
	    int rtn = 0; /* 返却値 */
	    
		ubyte c;
		for(int i; i < str.length; ++i) {
			c = cast(ubyte) str[i];
	
	        if (esc == 0) { /* ESC以外 */
	            if ((c >= 0x20) && (c <= 0x7E)) {
	                /* ASCII */
	            } else if ((c <= 0x06) || (c == 0x7f) || (c == 0xff)) {
	                rtn = -1; /* Binary */
	                break;
	            } else if (c == 0x1B) {
	                esc = 1; /* ESC (1B) */
	                rtn = 4; /* Not ASCII */
	                continue;
	            } else if ((c >= 0x07) && (c <= 0x0d)) {
	                /* ctrl */
	            } else if ((c >= 0x0e) && (c <= 0x1F)) {
	                rtn = -1; /* Binary */
	                break;
	            } else if (((c >= 0x80) && (c <= 0x8D))
	                    || ((c >= 0x90) && (c <= 0xA0))) {
	                rtn = 2; /* SJIS */
	                break;
	            } else if (c == 0xFE) {
	                rtn = 3; /* EUC */
	                break;
	            } else {
	                rtn = 4; /* Not ASCII */
	            }
	
	        } else if (esc == 1) { /* ESC (1B) */
	            if (c == 0x24) {
	                esc = 2; /* ESC (1B 24) */
	            } else if (c == 0x28) {
	                esc = 3; /* ESC (1B 28) */
	            } else {
	                esc = 0;
	            }
	
	        } else if (esc == 2) { /* ESC (1B 24) */
	            esc = 0;
	            if ((c == 0x40) || (c == 0x42)) {
	                rtn = 1; /* JIS */
	                break;
	            }
	
	        } else { /* ESC (1B 28) */
	            esc = 0;
	            if ((c == 0x42) || (c == 0x49) || (c == 0x4A)) {
	                rtn = 1; /* JIS */
	                break;
	            }
	        }
	    }
	
	    return(rtn);
	}
	
	static charcode getCode(char[] str) {
	    int rtn; /* 返却値 */
	    int rtn_sjis, rtn_euc;
	    ubyte c;
	    int i;
	
	    rtn = getCodeFast(str);
	    if (rtn != 4) {
	    	goto end;
	    }
	
	    rtn_sjis = checkSjis(str);
	    rtn_euc  = checkEuc(str);
	
	    if (rtn_sjis >= 0) {
	        if (rtn_euc >= 0) {
	            rtn = 4; /* Unknown (SJIS/EUC) */
	            if (!(rtn_sjis & 1)) { /* 漢字なしの場合 */
	                rtn = 3; /* EUC */
	            }
	        } else {
	            rtn = 2; /* SJIS */
	        }
	    } else {
	        if (rtn_euc >= 0) {
	            rtn = 3; /* EUC */
	        } else {
	            rtn = -1; /* Binary */
	        }
	    }
	
	end:
	    return cast(charcode) rtn;
	}
	
private:
	
	/// アスキーか
	static int checkAscii(char[] str) {
	    int rtn; /* 返却値 */
	    rtn = 0;
	
		ubyte c;
		for(int i; i < str.length; ++i) {
			c = cast(ubyte) str[i];
	
	        if ((c >= 0x07) && (c <= 0x0d)) {
	            /* ctrl */
	        } else if ((c >= 0x20) && (c <= 0x7E)) {
	            /* ASCII */
	        } else {
	            rtn = -1;
	            break;
	        }
	    }
	
	    return(rtn);
  	}
	
	/// JISか
	static int checkJis(char[] str) {
	    int esc; /* ESCの状態 */
	    int stat; /* 最終バイトの状態 */
	    int kind; /* 文字コードの状態 */
	    int rtn; /* 返却値 */
	
	    rtn = 0; /* ASCII */
	    esc = 0;
	    stat = 0;
	    kind = 0;
		
		ubyte c;
		for(int i; i < str.length; ++i) {
			c = cast(ubyte) str[i];
	    
	        if (esc == 0) { /* ESC以外 */
	            if (c == 0x1B) {
	                esc = 1; /* ESC (1B) */
	                continue;
	            }
	
	            if (kind == 0) { /* ASCII */
	                if ((c >= 0x07) && (c <= 0x0d)) {
	                    /* ctrl */
	                } else if ((c >= 0x20) && (c <= 0x7E)) {
	                    /* ASCII */
	                } else {
	                    rtn = -1; /* Not JIS */
	                    break;
	                }
	            } else if (kind == 1) { /* JISカナ */
	                if ((c >= 0x21) && (c <= 0x5F)) {
	                    /* JISカナ */
	                } else {
	                    rtn = -1; /* Not JIS */
	                    break;
	                }
	            } else { /* JIS漢字 */
	                if (stat == 0) { /* 1バイト目 */
	                    stat = 1;
	                    if ((c >= 0x21) && (c <= 0x7E)) {
	                        if ((c == 0x2D) || ((c >= 0x79) && (c <= 0x7C))) {
	                            rtn = rtn | 4; /* JIS漢字(NEC拡張外字) */
	                        } else if (((c >= 0x29) && (c <= 0x2F)) ||
	                                   ((c >= 0x75) && (c <= 0x7E))) {
	                            rtn = rtn | 8; /* JIS漢字(機種依存) */
	                        }
	                    } else {
	                        rtn = -1; /* Not JIS */
	                        break;
	                    }
	                } else { /* 2バイト目 */
	                    stat = 0;
	                    if ((c >= 0x21) && (c <= 0x7E)) {
	                        /* JIS漢字 */
	                    } else {
	                        rtn = -1; /* Not JIS */
	                        break;
	                    }
	                }
	            }
	
	        } else if (esc == 1) { /* ESC (1B) */
	            if (c == 0x24) {
	                esc = 2; /* ESC (1B 24) */
	            } else if (c == 0x28) {
	                esc = 3; /* ESC (1B 28) */
	            } else {
	                rtn = -1; /* Not JIS */
	                break;
	            }
	
	        } else if (esc == 2) { /* ESC (1B 24) */
	            esc = 0;
	            if ((c == 0x40) || (c == 0x42)) {
	                kind = 2; /* JIS漢字 */
	                rtn = rtn | 1; /* JIS漢字 */
	            } else {
	                rtn = -1; /* Not JIS */
	                break;
	            }
	
	        } else { /* ESC (1B 28) */
	            esc = 0;
	            if ((c == 0x42) || (c == 0x4A)) {
	                kind = 0; /* JISローマ字 */
	            } else if (c == 0x49) {
	                kind = 1; /* JISカナ */
	                rtn = rtn | 2; /* JISカナ */
	            } else {
	                rtn = -1; /* Not JIS */
	                break;
	            }
	        }
	    }
	
	    return(rtn);	
	}
	
	/// SJISか
	/* 返却値 */
	/*  0000: ASCII */
	/*  0001: JIS漢字 */
	/*  0010: JISカナ */
	/*  0100: JIS漢字(NEC拡張外字) */
	/*  1000: JIS漢字(機種依存) */
	/*    -1: NG */	
	static int checkSjis(char[] str) {
	    int stat; /* 最終バイトの状態 */
	    int rtn; /* 返却値 */
	    ubyte c1;
	
	    rtn = 0; /* ASCII */
	    stat = 0;

		ubyte c;
		for(int i; i < str.length; ++i) {
			c = cast(ubyte) str[i];

	        if (stat == 0) { /* 文字の1バイト目 */
	            if ((c >= 0x07) && (c <= 0x0d)) {
	                /* ctrl */
	            } else if ((c >= 0x20) && (c <= 0x7E)) {
	                /* ASCII */
	            } else if ((c >= 0xA1) && (c <= 0xDF)) {
	                rtn = rtn | 2; /* JISカナ */
	            } else if ((c >= 0x81) && (c <= 0x9F)) {
	                stat = 1; /* JIS漢字(81-9F) */
	                c1 = c;
	                rtn = rtn | 1;
	            } else if ((c >= 0xE0) && (c <= 0xEF)) {
	                stat = 2; /* JIS漢字(E0-EF) */
	                c1 = c;
	                rtn = rtn | 1;
	            } else {
	                rtn = -1; /* Not SJIS */
	                break;
	            }
	
	        } else if (stat == 1) { /* JIS漢字(81-9F)の2バイト目 */
	            if (((c >= 0x40) && (c <= 0x7E)) ||
	                ((c >= 0x80) && (c <= 0xFC))) {
	                stat = 0;
	                if ((c1 == 0x87) && (c < 0x9E)) {
	                    rtn = rtn | 4; /* JIS漢字(NEC拡張外字) */
	                } else if ((c1 >= 0x85) && (c1 <= 0x87)) {
	                    rtn = rtn | 8; /* JIS漢字(機種依存) */
	                } else if ((c1 == 0x88) && (c < 0x9E)) {
	                    rtn = rtn | 8; /* JIS漢字(機種依存) */
	                }
	            } else {
	                rtn = -1; /* Not SJIS */
	                break;
	            }
	
	        } else { /* JIS漢字(E0-EF)の2バイト目 */
	            if (((c >= 0x40) && (c <= 0x7E)) ||
	                ((c >= 0x80) && (c <= 0xFC))) {
	                stat = 0;
	                if ((c1 >= 0xED) && (c1 <= 0xEE)) {
	                    rtn = rtn | 4; /* JIS漢字(NEC拡張外字) */
	                } else if ((c1 >= 0xEB) && (c1 <= 0xEF)) {
	                    rtn = rtn | 8; /* JIS漢字(機種依存) */
	                }
	            } else {
	                rtn = -1; /* Not SJIS */
	                break;
	            }
	        }
	    }
	
	    return(rtn);
	}
	
	/// EUCか
	static int checkEuc(char[] str) {
	    int rtn; /* 返却値 */
	    rtn = 0; /* ASCII */
	    int stat = 0;
	
		ubyte c;
		for(int i; i < str.length; ++i) {
			c = cast(ubyte) str[i];
	
	        if (stat == 0) { /* 文字の1バイト目 */
	            if ((c >= 0x07) && (c <= 0x0d)) {
	                /* ctrl */
	            } else if ((c >= 0x20) && (c <= 0x7E)) {
	                /* ASCII */
	            } else if (c == 0x8E) {
	                stat = 1; /* JISカナ */
	                rtn = rtn | 2; /* JISカナ */
	            } else if ((c >= 0xA1) && (c <= 0xFE)) {
	                stat = 2; /* JIS漢字 */
	                rtn = rtn | 1; /* JIS漢字 */
	                if ((c == 0xAD) || ((c >= 0xF9) && (c <= 0xFC))) {
	                    rtn = rtn | 4; /* JIS漢字(NEC拡張外字) */
	                } else if (((c >= 0xA9) && (c <= 0xAF)) ||
	                           ((c >= 0xF5) && (c <= 0xFE))) {
	                    rtn = rtn | 8; /* JIS漢字(機種依存) */
	                }
	            } else {
	                rtn = -1;
	                break;
	            }
	
	        } else if (stat == 1) { /* JISカナの2バイト目 */
	            if ((c >= 0xA1) && (c <= 0xFE)) {
	                stat = 0;
	            } else {
	                rtn = -1;
	                break;
	            }
	
	        } else { /* JIS漢字の2バイト目 */
	            if ((c >= 0xA1) && (c <= 0xFE)) {
	                stat = 0;
	            } else {
	                rtn = -1;
	                break;
	            }
	        }
	    }
	
	    return(rtn);
	}
	
}
