/*
 * 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 <= ( wchar_t ) 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(
                cx,
                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) */

