/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
#if defined(USE_UTF8) || defined(USE_SJIS)
/* Header file */
#include "js-utils.h"
#include "js-stringruntime.h"
#include "js-exceptionhandler.h"
#include "js-contextp.h"
#include "js-evaluatorexception.h"
#include "js-runtimeexception.h"

/* Class data */
#ifdef USE_UTF8
#define	LeadingMask(n) ((0xfe << (n)) & 0xff)
#define	Mask(n)	(0xff >> (8 - (n)))
const JSStringRuntime::utf8Pattern_t JSStringRuntime::patterns[JSStringRuntime::_MB_CUR_MAX] = {
    {LeadingMask(7), Mask(7)},  /* 0xxxxxxx */
    {LeadingMask(5), Mask(5)},  /* 110xxxxx */
    {LeadingMask(4), Mask(4)},  /* 1110xxxx */
    {LeadingMask(3), Mask(3)},  /* 11110xxx */
    {LeadingMask(2), Mask(2)},  /* 111110xx */
    {LeadingMask(1), Mask(1)},  /* 1111110x */
};
const JSStringRuntime::utf8Range_t JSStringRuntime::ranges[JSStringRuntime::_MB_CUR_MAX] = {
#define UCS(n)  (((uint32_t) ~0) >> (8 * sizeof (int32_t) - (n)))
    {UCS(7), LeadingMask(7), Mask(7)},  /* 0x00000000 - 0x0000007f */
    {UCS(11), LeadingMask(5), Mask(5)}, /* 0x00000080 - 0x000007ff */
    {UCS(16), LeadingMask(4), Mask(4)}, /* 0x00000800 - 0x0000ffff */
    {UCS(21), LeadingMask(3), Mask(3)}, /* 0x00010000 - 0x001fffff */
    {UCS(26), LeadingMask(2), Mask(2)}, /* 0x00200000 - 0x03ffffff */
    {UCS(31), LeadingMask(1), Mask(1)}, /* 0x04000000 - 0x7fffffff */
#undef UCS
};
#undef Mask
#undef LeadingMask
#endif /* USE_UTF8 */

/* Procedures */
int
 JSStringRuntime::isUTF8String(jsChar_t * s, int len)
{
    const int startIndex = 2;   // for better performance

    if (len > 0) {
        int c = *s & 0xff;
        int index, n;

        index = startIndex;
        for (n = TableSize(patterns); n > 0; --n) {
            if ((c & ~patterns[index].mask) == patterns[index].leadingBits) {
                break;
            }
            index -= 1;
            if (index < 0)
                index = TableSize(patterns) - 1;
            if (index == startIndex)
                break;
        }
        if (n > 0) {
            len -= 1;
            if (len < index)
                return 0;
            for (int i = 0; i < index; ++i) {
                c = (s[i + 1] & ~0x3f) & 0xff;

                if (c != 0x80)
                    return -1;
            }
        }
        return (index + 1);
    }
    return 0;
}
int JSStringRuntime::isSJString(jsChar_t * s, int len)
{
    if (len > 0) {
        int lead = *s++ & 0xff;

        len -= 1;
        if ((lead >= 0x81 && lead <= 0x9f)
                || (lead >= 0xe0 && lead <= 0xfe)) {
            if (len > 0) {
                int second = *s & 0xff;

                if (second >= 0x40 && second <= 0xfc) {
                    return 2;
                }
            }
            else {
                return 0;
            }
        }
        else if (lead < 0x80) {
            return 1;
        }
        return -1;
    }
    return 0;
}
int JSStringRuntime::mbtowc(wchar_t * pwc, const char *s, size_t n)
{
#ifdef USE_UTF8
    if (n > 0) {
        int c = *s & 0xff;
        int index;

        c = *s & 0xff;
        for (index = 0; index < (int)TableSize(patterns); ++index) {
            if ((c & ~patterns[index].mask) == patterns[index].leadingBits) {
                break;
            }
        }
        if (index >= (int)TableSize(patterns))
            return -1;
        if (index >= (int)n)
            return -1;

        int val = *s & patterns[index].mask;

        switch (index) {
        case 5:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 4:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 3:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 2:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 1:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
            break;
        }
        *pwc = val;
        return index + 1;
    }
    *pwc = 0;
    return 0;
#else
    ->on other environment < -
#endif
}
int JSStringRuntime::wctomb(char *s, wchar_t wc)
{
#ifdef USE_UTF8
    int index;

    for (index = 0; index < (int)TableSize(ranges); ++index) {
        if (wc <= ranges[index].ucs)
            break;
    }
    if (index >= (int)TableSize(ranges))
        return -1;

    int length = index + 1;

    s += length - 1;
    switch (index) {
    case 5:
        *s = 0x80 | (wc & 0x3f);
        s -= 1;
        wc >>= 6;
    case 4:
        *s = 0x80 | (wc & 0x3f);
        s -= 1;
        wc >>= 6;
    case 3:
        *s = 0x80 | (wc & 0x3f);
        s -= 1;
        wc >>= 6;
    case 2:
        *s = 0x80 | (wc & 0x3f);
        s -= 1;
        wc >>= 6;
    case 1:
        *s = 0x80 | (wc & 0x3f);
        s -= 1;
        wc >>= 6;
        break;
    }
    *s = ranges[index].leadingBits | (wc & ranges[index].mask);
    return length;
#else
    ->on other environment < -
#endif
}
int JSStringRuntime::mblen(const char *s, size_t n)
{
#ifdef USE_UTF8
    if (n > 0) {
        int c = *s & 0xff;
        int index;

        for (index = 0; index < (int)TableSize(patterns); ++index) {
            if ((c & ~patterns[index].mask) == patterns[index].leadingBits) {
                break;
            }
        }

        if (index >= (int)TableSize(patterns))
            return -1;
        if (index >= (int)n)
            return -1;

        /*
           if((index >= (int) TableSize(patterns)) || (index >= (int) n)){
           JSContext *cx = JSContext::getCurrentContext();
           cx->exceptionHandler->throwEx(
           JSContext::reportRuntimeError(cx, JSContext::getMessage(cx,             "msg.illegal.character Shift-JIS")));
           }
         */

        return (index + 1);
    }
    return 0;
#else
    ->on other environment < -
#endif
}
int JSStringRuntime::mb_lentowc(wchar_t * pwc, const char *s, size_t n)
{
#ifdef USE_UTF8
    if (n > 0) {
        if (n > (int)TableSize(patterns))
            return -1;

        int val = *s & patterns[n - 1].mask;

        switch (n - 1) {
        case 5:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 4:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 3:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 2:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
        case 1:
            s += 1;
            val <<= 6;
            val |= *s & 0x3f;
            break;
        }
        *pwc = val;
        return n;
    }
    *pwc = 0;
    return 0;
#else
    ->on other environment < -
#endif
}
int JSStringRuntime::mb_strlen(const char *s)
{
    int len;

    len = 0;
    while (*s != '\0') {
        int result;

        result = mblen(s, _MB_CUR_MAX);
        if (result < 0)
            break;
        s += result;
        len += 1;
    }
    return len;
}
int JSStringRuntime::mb_postobpos(const char *s, int pos)
{
    int bpos;

    bpos = 0;
    for (int i = 0; i < pos; ++i) {
        int result;

        result = mblen(s + bpos, _MB_CUR_MAX);
        if (result < 0)
            return -1;
        bpos += result;
    }
    return bpos;
}
int JSStringRuntime::mb_bpostopos(const char *s, int bpos)
{
    int pos, i;

    pos = 0;
    for (i = 0; i < bpos;) {
        int result;

        result = mblen(s + i, _MB_CUR_MAX);
        if (result < 0)
            return -1;
        i += result;
        pos += 1;
    }
    return (i == bpos ? pos : -1);
}

jsString_t JSStringRuntime::mb_substring(JSContext * cx, jsString_t string, int beginIndex, int endIndex)
{
    int i, start, end;

    start = 0;
    for (i = 0; i < beginIndex; ++i) {
        start += mblen(string + start, _MB_CUR_MAX);
    }
    end = start;
    for (; i < endIndex; ++i) {
        end += mblen(string + end, _MB_CUR_MAX);
    }
    return JS_Substring(cx, string, start, end);
}

#endif /* defined(USE_UTF8) || defined(USE_SJIS) */
