/****************************************************************************
 * 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

/* ======================================================================== */
/* [structs] */

void
knh_Writer_struct_init(Ctx *ctx, Struct *s1, int init, Object *cs)
{
//	Writer *b =  (Writer*)s1;
//	Writer *b2 = (Writer*)s2;
	TODO();
	//b2->flag  =     b->flag;
	//KNH_INITv(b2->name, b->name);
}

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

void
knh_Writer_struct_copy(Ctx *ctx, Struct *s1, Struct *s2)
{
//	Writer *b =  (Writer*)s1;
//	Writer *b2 = (Writer*)s2;
	TODO();
	//b2->flag  =     b->flag;
	//KNH_INITv(b2->name, b->name);
}

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

#define _knh_Writer_struct_compare  NULL

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

void
knh_Writer_struct_traverse(Ctx *ctx, Struct *s, f_gc gc)
{
	Writer *b = (Writer*)s;
	gc(ctx, b->out);
	gc(ctx, b->strbuf);
	gc(ctx, b->tab);
	gc(ctx, b->lineFeed);
}

/* ======================================================================== */
/* [constructors] */


Writer* knh_Writer_new(Ctx *ctx, Writer *b, OutputStream *out, String *tab)
{
	b->indent = 0;
	KNH_INITv(b->out, out);
	if(IS_NULL(tab)) tab = knh_String_TAB();
	KNH_INITv(b->strbuf, Null);
	b->strpos = 0;
	KNH_INITv(b->tab, knh_String_TAB());
	KNH_INITv(b->lineFeed, knh_String_NL());
	return b;
}

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

Writer *new_Writer(Ctx *ctx, OutputStream *out) 
{
	Writer* b = (Writer*)knh_Object_malloc(ctx, CLASS_Writer);
	return knh_Writer_new(ctx, b, out, Null);
}

#define _new_Writer_StdOut(ctx)   new_Writer(ctx, System_out(ctx))
#define _new_Writer_StdErr(ctx)   new_Writer(ctx, System_err(ctx))

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

Writer *new_Writer__Bytes(Ctx *ctx, Bytes *ba) 
{
	Writer* b = (Writer*)knh_Object_malloc(ctx, CLASS_Writer);
	knh_Writer_new(ctx, b, new_OutputStream__Bytes(ctx, Null, ba), Null);
	if(IS_NOTNULL(ba)) {
		KNH_SETv(ctx, b->strbuf, ba);
		b->strpos = knh_Bytes_size(ba);
	}
	return b;
}

/* ======================================================================== */
/* [methods] */

INLINE
void knh_putc(Ctx *ctx, Writer *b, int_byte_t ch)
{
	knh_OutputStream_putc(ctx, b->out, ch);
}

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

/* @method void Writer.write(String s) */

INLINE
void knh_Writer_write(Ctx *ctx, Writer *b, knh_bytes_t s)
{
	knh_OutputStream_write(ctx, b->out, s);
}

#define _knh_write(ctx,w,s)   knh_Writer_write(ctx,w,s)

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

/* @method void Writer.flush() */

INLINE
void knh_Writer_flush(Ctx *ctx, Writer *b)
{
	knh_OutputStream_flush(ctx, b->out);
}

#define _knh_flush(ctx,w)   knh_Writer_flush(ctx,w)

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

/* @method void Writer.writeLine(String s) */

void knh_Writer_writeLine(Ctx *ctx, Writer *b, knh_bytes_t s)
{
	if(s.len > 0) {
		knh_OutputStream_write(ctx, b->out, s);
	}
	knh_OutputStream_write(ctx, b->out, knh_String_tobytes(b->lineFeed));
	knh_OutputStream_flush(ctx, b->out);
}

#define _knh_writeLine(ctx,w,s)   knh_Writer_writeLine(ctx,w,s)

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

/* @method void Writer.writeTab(Int n=1) */


void knh_Writer_writeTab(Ctx *ctx, Writer *b, knh_int_t n)
{
	knh_int_t i;
	for(i = 0; i < n; i++)
		knh_OutputStream_write(ctx, b->out, knh_String_tobytes(b->tab));
}

#define _knh_writeTab(ctx,w,n)   knh_Writer_writeTab(ctx,w,n)

/* ======================================================================== */
/* [datatype] */

void knh_write__p(Ctx *ctx, Writer *b, void *ptr)
{
	char buf[40];
	knh_snprintf(buf, sizeof(buf), "%p", ptr);
	knh_Writer_write(ctx, b, B(buf));
}

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

INLINE
void knh_write__s(Ctx *ctx, Writer *b, char *s)
{
	knh_Writer_write(ctx, b, B(s));
}

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

void knh_write__ifmt(Ctx *ctx, Writer *b, char *fmt, knh_int_t n)
{
	char buf[40];
	knh_snprintf(buf, sizeof(buf), fmt, n);
	knh_Writer_write(ctx, b, B(buf));
}

#define _knh_write__i(ctx, b, n) knh_write__ifmt(ctx, b, KNH_INT_FMT, n)
#define _knh_write__u(ctx, b, n) knh_write__ifmt(ctx, b, KNH_UINT_FMT, n)
#define _knh_write__x(ctx, b, n) knh_write__ifmt(ctx, b, KNH_INT_FMTX, n)

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

void knh_write__ffmt(Ctx *ctx, Writer *b, char *fmt, knh_float_t n)
{
	char buf[40];
	knh_snprintf(buf, sizeof(buf), fmt, n);
	knh_Writer_write(ctx, b, B(buf));
}

#define _knh_write__f(ctx, b, f)  knh_write__ffmt(ctx, b, KNH_FLOAT_FMT, f)
#define _knh_write__e(ctx, b, f)  knh_write__ffmt(ctx, b, KNH_FLOAT_FMTE, f)

/* ------------------------------------------------------------------------ */
/* [flag] */

void knh_write__flag(Ctx *ctx, Writer *b, knh_flag_t flag)
{
	knh_int_t i;
	knh_flag_t f = KNH_FLAG15;
	for(i = 0; i < 16; i++) {
		if(i > 0 && i % 8 == 0) knh_putc(ctx, b, ' ');
		if((f & flag) == f) {
			knh_putc(ctx, b, '1');
		}else{
			knh_putc(ctx, b, '0');
		}
		f = f >> 1;
	}
}

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

void knh_write__type(Ctx *ctx, Writer *b, knh_type_t type)
{
	knh_Writer_write(ctx, b, B(TYPEN(type)));
	knh_Writer_write(ctx, b, B(TYPEQ(type)));	
}

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

void knh_write__class(Ctx *ctx, Writer *b, knh_class_t cid)
{
	knh_Writer_write(ctx, b, B(TYPEN(cid)));
	if(TYPE_ISPLURAL(cid)) {
		knh_write(ctx, b, STEXT(".."));	
	}
}

#define _knh_write__expt(ctx, b, eid)    knh_write__s(ctx, b, EXPTN(eid))

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

void knh_write__mn(Ctx *ctx, Writer *b, knh_methodn_t mn)
{
	char buf[CLASSNAME_BUFSIZ];
	knh_format_methodn(buf, sizeof(buf), mn);
	knh_write__s(ctx, b, buf);
}

#define _knh_write__fn(ctx, b, fn)   knh_write__s(ctx, b, FIELDN(fn))


/* ======================================================================== */
/* [option] */

INLINE
void knh_write_delim(Ctx *ctx, Writer *b)
{
	knh_Writer_write(ctx, b, STEXT(", "));
}	

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

INLINE
void knh_write_dots(Ctx *ctx, Writer *b)
{
	knh_Writer_write(ctx, b, STEXT("..."));
}

/* ======================================================================== */
/* [indent] */

void knh_Writer_indent_inc(Ctx *ctx, Writer *b)
{
	b->indent++;
}	

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

void knh_Writer_indent_dec(Ctx *ctx, Writer *b)
{
	DEBUG_ASSERT(b->indent > 0);
	b->indent--;
}	

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

void knh_Writer_write_indent(Ctx *ctx, Writer *b, knh_bytes_t tab)
{
	int i;
	for(i = 0; i < b->indent; i++) {
		knh_write(ctx, b, tab);
	}
}	

#define _knh_write_indent(ctx, b, tab)   knh_Writer_write_indent(ctx, b, tab)

/* ======================================================================== */
/* [String] */

INLINE
void knh_format(Ctx *ctx, Writer *b, knh_methodn_t mn, Object *o, Any *m)
{
	Method *mtd = knh_Method_ufind(ctx, knh_Object_cid(o), mn);
	if(IS_NULL(mtd)) return ;
	/* ebp[-4] */
	KNH_PUSHv(ctx, mtd);    /* ebp[-3] */
	KNH_PUSHv(ctx, o);      /* ebp[-2] */
	KNH_PUSHv(ctx, b);      /* ebp[-1] */
	KNH_PUSHv(ctx, m);      /* ebp[0]  */
	KNH_SCALL(ctx, 3);
	VM_SHIFT(ctx, -1);
}

#define _knh_write__O(ctx, w, o)    knh_format(ctx,w,METHODN__s,o,Null)

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

String* 
knh_Object_movableText(Ctx *ctx, Object *b, knh_methodn_t mn, Any *m)
{
	knh_buffer_t cb = knh_Context_buffer(ctx);
	knh_format(ctx, cb.w, mn, b, m);
	return new_String__buffer(ctx, CLASS_String, cb);
}

#define _knh_Object_toString(ctx, b)   knh_Object_movableText(ctx, b, METHODN__s, Null)

///* ------------------------------------------------------------------------ */
//
//String* knh_Iterator_toString(Ctx *ctx, Object *b, knh_methodn_t mn, Any *m)
//{
//	knh_buffer_t cb = knh_Context_buffer(ctx);
//	if(IS_Iterator(b)) {
//		knh_format(ctx, cb.w, mn, b, m);
//	}else{
//		KNH_LOPEN(ctx,0);
//		Iterator *it = new_Iterator(ctx, knh_Object_cid(b), b, NULL);
//		KNH_LPUSH(ctx, it);
//		knh_format(ctx, cb.w, mn, it, m);
//		KNH_LCLOSE(ctx);
//	}
//	return new_String__buffer(ctx, CLASS_String, cb);
//}

/* ======================================================================== */
/* [printf] */

void knh_printf(Ctx *ctx, Writer *b, char *fmt, ...) 
{
	va_list args; 
	int ch; char *c = fmt;
	va_start(args , fmt);
	while((ch = *c) != '\0') {
		c++;
		switch(ch) {
		case '\\':
			ch = *c++;
			switch(ch) {
				case '\0' : return ;
				case 'n': knh_writeLine(ctx, b, STEXT("")); break;
				case 't': knh_writeTab(ctx, b, 1); break;
				default:  
					knh_putc(ctx, b, '\\');
					knh_putc(ctx, b, ch); 
			}
			break;
		case '%':
			ch = *c++;
			switch(ch) {
				case '\0' : return ;
				case 'd': 
					knh_write__i(ctx, b, (knh_int_t)va_arg(args, knh_int_t)); 
					break;
					
				case 'u': 
					knh_write__u(ctx, b, (knh_uint_t)va_arg(args, knh_uint_t)); 
					break;

				case 'x':
					knh_write__x(ctx, b, (knh_uint_t)va_arg(args, knh_uint_t)); 
					break;

				case 'f': 
					knh_write__f(ctx, b, (knh_float_t)va_arg(args, double)); 
					break;

				case 'e': 
					knh_write__e(ctx, b, (knh_float_t)va_arg(args, double)); 
					break;

				case 's': 
					knh_write__s(ctx, b, (char*)va_arg(args, char*)); 
					break;

				case 'p': 
					knh_write__p(ctx, b, (void*)va_arg(args, void*)); 
					break;

				case 'F': 
					knh_write__flag(ctx, b, (knh_flag_t)va_arg(args, int)); 
					break;

				case 'N': 
					knh_write__s(ctx, b, FIELDN((knh_fieldn_t)va_arg(args, int))); 
					break;

				case 'M': 
					knh_write__mn(ctx, b, (knh_methodn_t)va_arg(args, int)); 
					break;

				case 'C': 
					knh_write__class(ctx, b, (knh_class_t)va_arg(args, int)); 
					break;

				case 'T': 
					knh_write__type(ctx, b, (knh_methodn_t)va_arg(args, int)); 
					break;

				case 'B':
					knh_write(ctx, b, (knh_bytes_t)va_arg(args, knh_bytes_t));
					break;

				case 'O': 
					knh_write__O(ctx, b, (Object*)va_arg(args, Object*));
					break;

				default:
					knh_putc(ctx, b, '%'); 
					knh_putc(ctx, b, ch); 
			}
			break; 
		default:
			knh_putc(ctx, b, ch); 
		}
	} /* while */
	va_end(args);
}

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




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

#ifdef __cplusplus
}
#endif
