var REPLACEMENT_CHAR = 0xfffd;

/*
 * urlencoded された Shift_JIS の文字列を
 * urlencoded された UTF-8 の文字列に変換する
 */
function u8fromsjis(sjis) {
    var ucs = sjistoucs(sjis);
    return UTF8URIEncodedFromUCS(ucs);
}

/*
 * urlencoded された EUC-JP の文字列を
 * urlencoded された UTF-8 の文字列に変換する
 */
function u8fromeuc(euc) {
    var ucs = euctoucs(euc);
    return UTF8URIEncodedFromUCS(ucs);
}

function URIEncodeByte(byte) {
    var s = '0' + byte.toString(16);
    return '%' + s.slice(-2, s.length);
}

function UTF8URIEncodedFromUCS(points) {
    var str = '';
    for (var i = 0; i < points.length; ++i) {
        var cp = points[i];
        if (cp <= 0x7f) {
            str += URIEncodeByte(cp)
        }
        else if (cp <= 0x7ff) {
            str += URIEncodeByte(0xc0 | (cp >> 6) & 0x1f) +
                URIEncodeByte(0x80 | cp & 0x3f);
        }
        else if (cp <= 0xffff) {
            str += URIEncodeByte(0xe0 | (cp >> 12) & 0x0f) +
                URIEncodeByte(0x80 | (cp >> 6) & 0x3f) +
                URIEncodeByte(0x80 | cp & 0x3f);
        }
    }

    return str;
}

function bytesFromURIEncoded(str) {
    var bytes = [];
    var val;
    var len = str.length;
    for (var i = 0; i < len; ++i) {
        if (str.charAt(i) == '%') {
            if (len - 1 < i + 2) {
                throw new Error('invalid sequence');
            }
            val = parseInt('0x' + str.substr(i+1, 2));
            if (isNaN(val)) {
                throw new Error('invalid sequence');
            }
            i += 2;
        }
        else {
            val = str.charCodeAt(i);
        }

        bytes.push(val);
    }

    return bytes;
}


function sjistoucs(str) {
    try {
        var bytes = bytesFromURIEncoded(str);
    }
    catch (e) {
        alert(e.message);
        return [];
    }

    var map = getCP932toUCSMap();
    var len = bytes.length;
    var ret = [];
    for (var i = 0; i < len; ++i) {
        var c = bytes[i];
        if (c <= 0x7f) {
            ret.push(c);
        }
        else if (0x80 <= c && c <= 0xfc) {
            var u = REPLACEMENT_CHAR;
            if (i + 1 < len && 0x40 <= bytes[i+1] && bytes[i+1] <= 0xfc) {
                u = 0;
                i += 1;
                var hi = c;
                var low = bytes[i];
                var pos = (hi - (0xe0 <= hi ? 0xc1 : 0x81)) * 188;
                pos += low - (0x80 <= low ? 0x41 : 0x40);
                if (pos < 11280) {
                    pos *= 2;
                    u = (map.charCodeAt(pos) << 8) | map.charCodeAt(pos + 1);
                }

                if (u == 0) {
                    // unknown SJIS character
                    u = REPLACEMENT_CHAR;
                }
            }
            ret.push(u);
        }
        else if (0xa0 <= c && c <= 0xdf) {
            ret.push(0xff00 | (c - 0x40));
        }
        else {
            // invalid character
        }
    }

    return ret;
}


function euctoucs(uriencodedString) {
    try {
        var bytes = bytesFromURIEncoded(uriencodedString);
    }
    catch (e) {
        return [];
    }

    var map = getEUCJPtoUCSMap();
    var hmap = getEUCJPHojotoUCSMap();
    var len = bytes.length;
    var ret = [];
    for (var i = 0; i < len; ++i) {
        var c = bytes[i];
        if (c <= 0x7f) {
            ret.push(c);
        }
        else if (0xa0 <= c && c <= 0xfe) {
            var u = REPLACEMENT_CHAR;
            if (i + 1 < len) {
                u = 0;
                i += 1;
                var hi = c & 0x7f;
                var low = bytes[i] & 0x7f;
                if (hi >= 32 && low >= 32) {
                    var pos = ((hi - 32) * 96 + (low - 32)) * 2;
                    u = (map.charCodeAt(pos) << 8) | map.charCodeAt(pos + 1);
                }

                if (u == 0) {
                    u = REPLACEMENT_CHAR;
                }
            }
            ret.push(u);
        }
        else if (c == 0x8e) {
            var u = REPLACEMENT_CHAR;
            if (i + 1 < len) {
                i += 1;
                var kana = bytes[i];
                if (0xa1 <= kana && kana <= 0xdf) {
                    u = 0xff00 | (kana - 0x40);
                }
            }
            ret.push(u);
        }
        else if (c == 0x8f) {
            var u = REPLACEMENT_CHAR;
            if (i + 2 < len) {
                u = 0;
                i += 1;
                var hi = bytes[i] & 0x7f;
                i += 1;
                var low = bytes[i] & 0x7f;
                if (hi >= 32 && low >= 32) {
                    var pos = ((hi - 32) * 96 + (low - 32)) * 2;
                    u = (hmap.charCodeAt(pos) << 8) | hmap.charCodeAt(pos + 1);
                }

                if (u == 0) {
                    u = REPLACEMENT_CHAR;
                }
            }
            ret.push(u);
        }
        else {
            ret.push(REPLACEMENT_CHAR);
        }
    }

    return ret;
}
