/****************************************************************************
 * KONOHA COPYRIGHT, LICENSE NOTICE, AND DISCRIMER  
 * 
 * Copyright (c) 2005-2008, Kimio Kuramitsu <kimio at ynu.ac.jp>
 *           (c) 2008-      Konoha Software Foundation  
 * All rights reserved.
 * 
 * You may choose one of the following two licenses when you use konoha. 
 * See www.konohaware.org/license.html for further information.
 * 
 * (1) GNU General Public License 2.0      (with    KONOHA_UNDER_GPL2)
 * (2) Konoha Software Foundation License 1.0
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *  
 ****************************************************************************/

/* ************************************************************************ */

#include"commons.h"

/* ************************************************************************ */

#ifdef __cplusplus 
extern "C" {
#endif

/* ======================================================================== */
/* [constructor] */

/* ------------------------------------------------------------------------ */
/* ======================================================================== */
/* [method] */

/* @method Boolean! String.equals(String! s) */

INLINE
knh_bool_t knh_String_equals(String *o, knh_bytes_t s)
{
	return (o->size == s.len && knh_strncmp((char*)o->str, (char*)s.buf, s.len) == 0);
}

/* ------------------------------------------------------------------------ */

/* @method Boolean! String.startsWith(String! s) */

INLINE
knh_bool_t knh_String_startsWith(String *b, knh_bytes_t s)
{
	return knh_bytes_startsWith(knh_String_tobytes(b), s);
}

/* ------------------------------------------------------------------------ */
/* @method Boolean! String.endsWith(String! s) */

INLINE
knh_bool_t knh_String_endsWith(String *b, knh_bytes_t s)
{
	return knh_bytes_endsWith(knh_String_tobytes(b), s);
}

/* ------------------------------------------------------------------------ */

static
knh_index_t knh_bytes_indexOf(knh_bytes_t base, knh_bytes_t delim)
{
	knh_uchar_t s = delim.buf[0];
	knh_index_t i;
	for(i = 0; i < base.len - delim.len; i++) {
		if(base.buf[i] == s) {
			int j;
			for(j = 1; j < delim.len; j++) {
				if(base.buf[i+j] != delim.buf[j]) break;
			}
			if(j == delim.len) return i;
		}
	}
	return -1;
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] Int! String.indexOf(String! s) */

METHOD knh__String_indexOf(Ctx *ctx, knh_sfp_t *sfp)
{
	METHOD_RETURN(ctx, sfp, new_Int(ctx, knh_bytes_indexOf(knh_String_tobytes(sfp[0].s), knh_String_tobytes(sfp[1].s))));
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC|NULLBASE] Int! String.getSize() */

METHOD knh__String_getSize(Ctx *ctx, knh_sfp_t *sfp)
{
	if(IS_NULL(sfp[0].o)) {
		METHOD_RETURN(ctx, sfp, knh_tInt[0 - KNH_TINT_MIN]);
	}
	else {
		String *s = (String*)sfp[0].o;
		if(knh_String_isASCII(s)) {
			METHOD_RETURN(ctx, sfp, new_Int(ctx, knh_String_strlen(s)));
		}
		else {
			METHOD_RETURN(ctx, sfp, new_Int(ctx, knh_bytes_mlen(knh_String_tobytes(s))));
		}
	}
}
	
/* ------------------------------------------------------------------------ */
/* @method[STATIC|NULLBASE] String! String.opAdd:2(Any v) */

METHOD knh__String_opAdd__2(Ctx *ctx, knh_sfp_t *sfp)
{
	knh_wbuf_t cb = knh_Context_wbuf(ctx);
	knh_format(ctx, cb.w, METHODN__s, sfp[0].o, KNH_NULL);
	knh_format(ctx, cb.w, METHODN__s, sfp[1].o, KNH_NULL);
	METHOD_RETURN(ctx, sfp, new_String__wbuf(ctx, cb));
}

/* ------------------------------------------------------------------------ */
/* @method[VARARGS|STATIC|NULLBASE] String! String.opAdd(Any v) */

METHOD knh__String_opAdd(Ctx *ctx, knh_sfp_t *sfp)
{
	knh_sfp_t *v = sfp + 1;
	knh_vargc_t ac = knh_sfp_argc(ctx, v);
	int i;
	knh_wbuf_t cb = knh_Context_wbuf(ctx);
	knh_format(ctx, cb.w, METHODN__s, sfp[0].o, KNH_NULL);
	for(i = 0; i < ac; i++) {
		knh_format(ctx, cb.w, METHODN__s, v[i].o, KNH_NULL);
	}
	METHOD_RETURN(ctx, sfp, new_String__wbuf(ctx, cb));
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] String! String.opSub:2(String! t) */

METHOD knh__String_opSub__2(Ctx *ctx, knh_sfp_t *sfp)
{
	knh_bytes_t base = knh_String_tobytes(sfp[0].s);
	knh_bytes_t t = knh_String_tobytes(sfp[1].s);
	knh_uchar_t c = t.buf[0];
	knh_wbuf_t wbuf = knh_Context_wbuf(ctx);
	knh_index_t i;
	for(i = 0; i < base.len - t.len; i++) {
		if(base.buf[i] == c) {
			int j;
			for(j = 1; j < t.len; j++) {
				if(base.buf[i+j] != t.buf[j]) break;
			}
			if(j == t.len) {
				i += t.len;
				continue;
			}
		}
		knh_Bytes_putc(ctx, wbuf.ba, base.buf[i]);
	}
	if(base.len == knh_wbuf_size(wbuf)) {
		knh_wbuf_clear(wbuf);
		METHOD_RETURN(ctx, sfp, sfp[0].o);
	}
	else {
		METHOD_RETURN(ctx, sfp, new_String__wbuf(ctx, wbuf));
	}
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] String! String.replace(String! old, String! new) */

METHOD knh__String_replace(Ctx *ctx, knh_sfp_t *sfp)
{
	TODO_THROW(ctx);
}

///* ------------------------------------------------------------------------ */
///* @method[STATIC] String! String.opMul(Int! n) */
//
//String* knh_String_opMul(Ctx *ctx, String *b, knh_int_t n)
//{
//	if(n <= 0) {
//		return TS_EMPTY;
//	}else if(n == 1) {
//		return b;
//	}
//	
//	String *s = (String*)new_Object__RAW(ctx, KNH_FLAG_String, CLASS_String, sizeof(String));
//	s->strlen = o->size * n;
//	s->value = (knh_uchar_t*)KNH_MALLOC(ctx, KNH_SIZE(s->strlen+1));
//	
//	knh_int_t i;
//	for(i = 0; i < n; i++) {
//		knh_memcpy(s->value + (o->size*i), o->str, o->size); 
//	}
//	s->value[s->strlen] = '\0';
//	s->hcode = knh_string_hash(s->value, s->strlen);
//	s->orig = NULL;
//	return s;
//}


/* ------------------------------------------------------------------------ */
/* @method[STATIC|NULLBASE] String! String.opDiv:2(String v) */

METHOD knh__String_opDiv__2(Ctx *ctx, knh_sfp_t *sfp)
{
	if(!IS_bString(sfp[0].o)) {
		METHOD_RETURN(ctx, sfp, TS_EMPTY);
	}
	else {
		knh_bytes_t base = knh_String_tobytes(sfp[0].s);
		knh_index_t index = knh_bytes_indexOf(base, knh_String_tobytes(sfp[1].s));
		if(index == -1) {
			METHOD_RETURN(ctx, sfp, sfp[0].o);
		}
		else {
			if(index == 0) {
				METHOD_RETURN(ctx, sfp, TS_EMPTY);
			}
			else {
				base.len = index;
				METHOD_RETURN(ctx, sfp, new_String(ctx, base, sfp[0].s));
			}
		}
	}
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC|NULLBASE] String! String.opDiv(String v) */

METHOD knh__String_opDiv(Ctx *ctx, knh_sfp_t *sfp)
{
	knh__String_opDiv__2(ctx, sfp);
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC|NULLBASE] String! String.opMod(String v) */

METHOD knh__String_opMod(Ctx *ctx, knh_sfp_t *sfp)
{
	if(!IS_bString(sfp[0].o)) {
		METHOD_RETURN(ctx, sfp, TS_EMPTY);
	}
	else {
		knh_bytes_t base = knh_String_tobytes(sfp[0].s);
		knh_bytes_t delim = knh_String_tobytes(sfp[1].s);
		knh_index_t index = knh_bytes_indexOf(base, delim);
		if(index == -1) {
			METHOD_RETURN(ctx, sfp, TS_EMPTY);
		}
		else {
			base.buf = base.buf + (index + delim.len);
			base.len = (base.len - index + delim.len) - 1;
			METHOD_RETURN(ctx, sfp, new_String(ctx, base, sfp[0].s));
		}
	}
}

/* ======================================================================== */
/* @method[STATIC] String! String.get(Int! n) */

METHOD knh__String_get(Ctx *ctx, knh_sfp_t *sfp)
{
	knh_bytes_t base = knh_String_tobytes(sfp[0].s);
	if(knh_String_isASCII(sfp[0].s)) {
		size_t n = knh_array_index(ctx, (sfp[1].i)->value, knh_String_strlen(sfp[0].s));
		base.buf = base.buf + n;
		base.len = 1;
		METHOD_RETURN(ctx, sfp, new_String(ctx, base, sfp[0].s));
	}
	else {
		size_t off = knh_array_index(ctx, (sfp[1].i)->value, knh_bytes_mlen(base));
		knh_bytes_t sub = knh_bytes_mofflen(base, off, 1);
		METHOD_RETURN(ctx, sfp, new_String(ctx, sub, sfp[0].s));
	}
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] String! String.opOffset(Int offset, Int len) */

METHOD knh__String_opOffset(Ctx *ctx, knh_sfp_t *sfp)
{
	knh_bytes_t base = knh_String_tobytes(sfp[0].s);
	if(knh_String_isASCII(sfp[0].s)) {
		size_t offset = IS_NULL(sfp[1].o) ? 0 : knh_array_index(ctx, (sfp[1].i)->value, base.len);
		if(IS_NULL(sfp[2].o)) {
			METHOD_RETURN(ctx, sfp, new_String(ctx, knh_bytes_last(base, offset), sfp[0].s));
		}
		else {
			knh_bytes_t sub = knh_bytes_offlen(base, offset, (sfp[2].i)->value);
			METHOD_RETURN(ctx, sfp, new_String(ctx, sub, sfp[0].s));
		}
	}
	else {
		TODO_THROW(ctx);
//		size_t mlen = knh_bytes_mlen(base);
//		size_t offset = IS_NULL(sfp[1].o) ? 0 : knh_array_index(ctx, (sfp[1].i)->value, mlen);
//		if(IS_NULL(sfp[2].o)) {
//			METHOD_RETURN(ctx, sfp, new_String(ctx, knh_bytes_last(base, offset), sfp[0].s));
//		}
//		else {
//			knh_bytes_t sub = knh_bytes_mofflen(base, offset, (sfp[2].i)->value);
//			METHOD_RETURN(ctx, sfp, new_String(ctx, sub, sfp[0].s));
//		}
	}
}

/* ======================================================================== */

String* knh_String_bconv(Ctx *ctx, String *s, f_bconv bconv)
{
	knh_bytes_t base = knh_String_tobytes(s);
	Bytes *ba = knh_Context_openBConvBuf(ctx);
	bconv(ctx, KNH_NULL, base, ba);
	if(knh_strncmp((char*)(base.buf), (char*)(ba->buf), base.len) == 0) {
		knh_Context_closeBConvBuf(ctx, ba);
		return s;
	}
	else {
		s = new_String(ctx, knh_Bytes_tobytes(ba), NULL);
		knh_Context_closeBConvBuf(ctx, ba);
		return s;
	}
}

/* ------------------------------------------------------------------------ */

static
size_t bconv_toLower(Ctx *ctx, BytesConv *o, knh_bytes_t t, knh_Bytes_t *ba)
{
	int i;
	for(i = 0; i < t.len; i++) {
		if(isupper(t.buf[i])) {
			knh_Bytes_putc(ctx, ba, tolower(t.buf[i]));
		}
		else {
			knh_Bytes_putc(ctx, ba, t.buf[i]);
		}
	}
	return t.len;
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] String! String.toLower() */

METHOD knh__String_toLower(Ctx *ctx, knh_sfp_t *sfp)
{
	METHOD_RETURN(ctx, sfp, knh_String_bconv(ctx, sfp[0].s, bconv_toLower));
}

/* ------------------------------------------------------------------------ */

static
size_t bconv_toUpper(Ctx *ctx, BytesConv *o, knh_bytes_t t, knh_Bytes_t *ba)
{
	int i;
	for(i = 0; i < t.len; i++) {
		if(islower(t.buf[i])) {
			knh_Bytes_putc(ctx, ba, toupper(t.buf[i]));
		}
		else {
			knh_Bytes_putc(ctx, ba, t.buf[i]);
		}
	}
	return t.len;
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] String! String.toUpper() */

METHOD knh__String_toUpper(Ctx *ctx, knh_sfp_t *sfp)
{
	METHOD_RETURN(ctx, sfp, knh_String_bconv(ctx, sfp[0].s, bconv_toUpper));
}

/* ------------------------------------------------------------------------ */
/* @method[STATIC] String! String.trim() */

METHOD knh__String_trim(Ctx *ctx, knh_sfp_t *sfp)
{
	knh_bytes_t t = knh_String_tobytes(sfp[0].s);
	size_t i, s = 0;
	for(i = 0; i < t.len; i++) {
		if(isspace(t.buf[i])) continue;
	}
	s = i;
	for(i = t.len - 1; s < i; i--) {
		if(isspace(t.buf[i])) continue;
	}
	if(i - s + 1 == t.len) {
		METHOD_RETURN(ctx, sfp, sfp[0].o)
	}
	t.buf = t.buf + s;
	t.len = i - s + 1;
	METHOD_RETURN(ctx, sfp, new_String(ctx, t, NULL));
}

/* ======================================================================== */
/* [movabletext] */

/* @method void String.%s(OutputStream w, Any m) */

INLINE
void knh_String__s(Ctx *ctx, String *b, OutputStream *w, Any *m)
{
	knh_write(ctx, w, knh_String_tobytes(b));
}

/* ------------------------------------------------------------------------ */

/* @method void String.%repr(OutputStream w, Any m) */

void knh_String__repr(Ctx *ctx, String *o, OutputStream *w, Any *m)
{
	int_byte_t quote = '\'';
	if(knh_Object_cid(o) == CLASS_String) quote = '"';
	knh_putc(ctx, w, quote);
	char *p = knh_String_tochar(o);
	knh_int_t i;
	for(i = 0; i < o->size; i++) {
		switch(p[i]) {
			case '\t' :
				knh_putc(ctx, w, '\\'); knh_putc(ctx, w, 't'); break ;
			case '\n' :
				knh_putc(ctx, w, '\\'); knh_putc(ctx, w, 'n'); break ;
			case '\r' :
				knh_putc(ctx, w, '\\'); knh_putc(ctx, w, 'r'); break ;
			case '\\' : case '\"' : case '\'' :
				knh_putc(ctx, w, '\\'); knh_putc(ctx, w, p[i]); break ;
			default :
				knh_putc(ctx, w, p[i]);
		}
	}
	knh_putc(ctx, w, quote);
}

/* ------------------------------------------------------------------------ */

/* ======================================================================== */
/* [mapping] */


/* ------------------------------------------------------------------------ */

#ifdef __cplusplus
}
#endif
