/*****
 NAME
 	ctext.m - source code for CText class
 VERSION
 	$Id$
 CHANGELOG
 	$Log$
 */

#include <coconut/ctext.h>
#include <coconut/cidlist.h>
#include <coconut/cstring.h>
#include <coconut/cerror.h>
#include <coconut/csystem.h>
#include <coconut/pstream.h>

@implementation CText

+ newTextFromStr: (id <PBasicStr>) str withNewline: (boolean) donewline
{
	id <PText>	newtext ;

	newtext = [[CText alloc] initText: donewline] ;
	[CSystem checkPtr: newtext] ;
	[newtext appendStr: str] ;
	return newtext ;
}

- init
{
	return [self initText: FALSE] ;
}

- initText: (boolean) donewline 
{
	line_list = [[CIdList alloc] init] ;
	print_with_newline = donewline ;
	[CSystem checkPtr: line_list] ;
	return [super init] ;
}

- (void) dealloc
{
	[line_list release] ;
	[super dealloc] ;
}

- (id <PString>) appendStr: (id <PBasicStr>) str
{
	return str ? [self appendPtr: [str ptr] length: [str length]] : nil ;
}

- (id <PString>) appendPtr: (const utf8_char *) text length: (u_int) len
{
	const utf8_char *	ptr ;
	const utf8_char *	head ;
	const utf8_char *	last ;
	id <PString>		laststr = nil ;
	utf8_char 		c ;

	head = text ; last = &text[len] ;
	for(ptr = head ; ptr<last ; ptr++){
		if((c = *ptr) == '\n'){
			laststr = [self appendLine: head length: ptr-head] ;
			if((head = ptr+1) >= last)
				return laststr ;
		}
	}
	return [self appendLine: head length: ptr-head] ;
}

- (id <PString>) appendLine: (const utf8_char *) text length: (u_int) len
{
	id <PString>		str ;

	str = [[CString alloc] initStringWithPage: 64] ;
	[CSystem checkPtr: str] ;
	[str setPtr: text length: len] ;
	[line_list append: str] ;
	[str release] ;
	return str ;
}

- (id <PString>) appendLineStr: (id <PString>) str
{
	[line_list append: str] ;
	return str ;
}

- (id <PString>) prependPtr: (const utf8_char *) text length: (u_int) len
{
	const utf8_char *	ptr ;
	const utf8_char *	head ;
	const utf8_char *	last ;
	id <PString>		laststr = nil ;
	utf8_char 		c ;

	/* sample:  0 1234 56789
		    \n0123\n456\n */
	head = text ; last = &text[len-1] ;
	ptr = *last == '\n' ? --last : last ;
	for( ; head<=ptr ; ptr--){
		if((c = *ptr) == '\n'){
			laststr = [self prependLine: ptr+1 length: last-ptr] ;
			last = ptr - 1 ;
		}
	}
	return [self prependLine: ptr+1 length: last-ptr] ;
}

- (id <PString>) prependLine: (const utf8_char *) text length: (u_int) len
{
	id <PString>	str ;

	str = [[CString alloc] initStringWithPage: 64] ;
	[CSystem checkPtr: str] ;
	[str setPtr: text length: len] ;
	[line_list prepend: str] ;
	[str release] ;
	return str ;
}

- (id <PString>) prependLineStr: (id <PString>) str
{
	[line_list prepend: str] ;
	return str ;
}

- (id <PString>) add: (id <PString>) str
{
	[line_list add: str] ;
	return str ;
}

- (id <PString>) addPtr: (const utf8_char *) text length: (u_int) len
{
	const utf8_char *	ptr ;
	const utf8_char *	head ;
	const utf8_char *	last ;
	id <PString>		laststr = nil ;
	utf8_char 		c ;

	head = text ; last = &text[len] ;
	for(ptr = head ; ptr<last ; ptr++){
		if((c = *ptr) == '\n'){
			laststr = [self addLine: head length: ptr-head] ;
			if((head = ptr+1) >= last)
				return laststr ;
		}
	}
	return [self addLine: head length: ptr - head] ;
}

- (id <PString>) addLine: (const utf8_char *) text length: (u_int) len
{
	id <PString>	str ;

	str = [[CString alloc] initStringWithPage: 64] ;
	[CSystem checkPtr: str] ;
	[str setPtr: text length: len] ;
	[line_list add: str] ;
	[str release] ;
	return str ;
}

- (id <PString>) currentItem
{
	return [line_list currentItem] ;
}

- (id <PString>) concatenate
{
	id <PString>	wholestr ;
	id 		line ;

	wholestr = [[CString alloc] initStringWithPage: 1024] ;
	line = [line_list moveToHead] ;
	for( ; line ; line = [line_list moveNext]){
		[wholestr appendStr: line] ;
		if(print_with_newline)
			[wholestr appendChar: '\n'] ;
	}
	return wholestr ;
}

- (id <PString>) moveNext
{
	return [line_list moveNext] ;
}

- (id <PString>) movePrev
{
	return [line_list movePrev] ;
}

- (id <PString>) moveToHead 
{
	return [line_list moveToHead] ;
}

- (id <PString>) moveToTail
{
	return [line_list moveToTail] ;
}

- removeLeftSpaces: (u_int) spaces tab: (u_int) tablen
{
	id <PString>	line ;
	u_int		charnum ;

	line = [line_list moveToHead] ;
	while(line){
		charnum = [CString stepSpaces: [line ptr] step: spaces
		  tab: tablen] ;
		[line remove: 0 length: charnum] ;
		line = [line_list moveNext] ;
	}
	return nil ;
}

- removeBothEmptyLines
{	
	id str ;

	/* remove the top */
	[line_list moveToHead] ;
	do {
		str = [line_list currentItem] ;
		if([str isEmpty])
			[line_list remove] ;
		else
			break ;
	} while(str) ;

	/* remove the bottom */
	[line_list moveToTail] ;
	do {
		str = [line_list currentItem] ;
		if([str isEmpty])
			[line_list remove] ;
		else
			break ;
	} while(str) ;
	return nil ;
}

- pretty: (const utf8_char *) unitptr indent: (id <PIndent>) indent
{
	id <PString>	line ;

	[self removeBothEmptyLines] ;
	/* insert first line's indent */
	if((line = [line_list moveToHead]) != nil){
		[line removeSideSpaces] ;
		[line prependPtr: unitptr] ;
		while((line = [line_list moveNext]) != nil){
			[line removeSideSpaces] ;
			[line prependPtr: [indent ptr]] ;
		}
	}
	return nil ;
}

- foreach: (SEL) msg with: p1 with: p2
{
	return [line_list foreach: msg with: p1 with: p2] ;
}

- clear
{
	return [line_list clear] ;
}

#define	STREAM	((id <PIndentStream>) stream)
- print: (id) stream
{
	id <PString>		line ;
	id <PIndentStream>	nlstream ;

	nlstream = print_with_newline ? STREAM : nil ;
	/* insert first line's indent */
	if((line = [line_list moveToHead]) != nil){
		do {
			[line print: STREAM] ; [nlstream putChar: '\n'] ;
		} while((line = [line_list moveNext]) != nil) ;
	}
	return nil ;
}

- doPrintWithNewline: (boolean) donewline
{
	print_with_newline = donewline ;
	return nil ;
}

@end

