/*
 * text.c
 *
 * Text class
 *
 * $Id :$
 * $Log:$
 *
 */

#include "defs.h"
#include "lines.h"
#include "text.h"
#include "memory.h"
#include <clib/alib_protos.h>


static LINE_PTR add_line( TEXT_PTR text, const STRPTR line_start, const STRPTR line_end, ERROR_TYPE *err );


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

/*
 * Text_New()
 *
 * PURPOSE: Constructor for text class
 *
 * INPUTS :
 *
 * RETURNS:
 */

TEXT_PTR
Text_New( const STRPTR contents, ERROR_TYPE *err_code )
{
	TEXT_PTR new_text;

	if( new_text = (TEXT_PTR) Memory_Alloc( sizeof( TEXT_TYPE ) ) )
	{
		NewList( (struct List *) &(new_text->Contents) );

		{
			STRPTR current_char = contents;
			STRPTR line_start   = contents;

			for( ; *current_char; current_char++ )
			{
				switch( *current_char )
				{
					case '\n' :
					case '\f' :
						/* found a line break */
						if( add_line( new_text, line_start, current_char + 1, err_code ) )
							line_start = current_char + 1;
						else
							;
					default:
						/* do nothing */
				}
			} /* for */

			if( current_char != line_start )
			{
				/* the last line didn't end in a break - so add it now */
				add_line( new_text, line_start, current_char + 1, err_code );
			}
		} /* block */

	} /* if */
	else
		*err_code = ERR_NO_MEMORY;

	return new_text;
}

/*
 * add_line()
 *
 * PURPOSE: add a line to a text object and bump line count
 *
 * INPUTS : text       - the text object
 *          line_start - ptr to start of line
 *          line_end   - ptr to end of line
 *
 * RETURNS: ptr to the new line if succcesful, NULL otherwise
 *
 */

static LINE_PTR
add_line( TEXT_PTR text, const STRPTR line_start, const STRPTR line_end, ERROR_TYPE *err_code )
{
	LINE_PTR new_line = NULL;

	if( text->Length < MAX_TEXT_LEN )
	{
		if( new_line = Line_New( line_start, line_end - line_start, err_code ) )
		{
			AddTail( (struct List *) &(text->Contents), (struct Node *) &(new_line->Node) );
			text->Length++;
		}
	}
	else
		*err_code = ERR_TOO_MANY_LINES;

	return new_line;
}


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

/*
 * Text_Dispose()
 *
 * PURPOSE: Destructor for text class
 *
 * INPUTS:
 *
 * RETURNS: None
 */

VOID
Text_Dispose( TEXT_PTR text )
{
	LINE_PTR      l1 = (LINE_PTR) text->Contents.mlh_Head;
	LINE_PTR      l2;
	TEXT_LEN_TYPE i  = text->Length;

	for( ; i; i-- )
	{
		l2 = (LINE_PTR) l1->Node.mln_Succ;
		Line_Dispose( l1 );
		l1 = l2;
	}

	Memory_Free( text, sizeof( TEXT_TYPE ) );
}


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

/*
 * Text_GetTopLine()
 *
 * PURPOSE:
 *
 * INPUTS :
 *
 * RETURNS:
 */

LINE_PTR
Text_GetTopLine( const TEXT_PTR text )
{
	return text->Length ? (LINE_PTR) text->Contents.mlh_Head : NULL;
}


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

/*
 * Text_GetBottomLine()
 *
 * PURPOSE:
 *
 * INPUTS :
 *
 * RETURNS:
 */

LINE_PTR
Text_GetBottomLine( const TEXT_PTR text )
{
	return text->Length ? (LINE_PTR) text->Contents.mlh_TailPred : NULL;
}


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

/*
 * Text_GetNextLine()
 *
 * PURPOSE: Get next Line in a linked list of Line objects.
 *
 * INPUTS : line - the Line to find the successor of.
 *
 * RETURNS: pointer to the sucessor if there is one, else NULL
 */

LINE_PTR
Text_GetNextLine( const TEXT_PTR text, const LINE_PTR line )
{
	LINE_PTR l = (LINE_PTR) line->Node.mln_Succ;

	return (l->Node.mln_Succ) ? l : NULL;
}


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

/*
 * Text_GetPrevLine()
 *
 * PURPOSE: Get previous Line in a linked list of Line objects.
 *
 * INPUTS : line - the Line to find the predecessor of.
 *
 * RETURNS: pointer to the predecessor if there is one, else NULL
 */

LINE_PTR
Text_GetPrevLine( const TEXT_PTR text, const LINE_PTR line )
{
	LINE_PTR l = (LINE_PTR) line->Node.mln_Pred;

	return l->Node.mln_Pred ? l : NULL;
}



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

/*
 * Text_GetNthLine()
 *
 * PURPOSE:
 *
 * INPUTS :
 *
 * RETURNS:
 */

LINE_PTR
Text_GetNthLine( const TEXT_PTR text, TEXT_LEN_TYPE line_no )
{
	LINE_PTR line = NULL;

	if( line_no < text->Length )
	{
		LINE_LEN_TYPE i = 0;
		line = (LINE_PTR) text->Contents.mlh_Head;

		while( i++ < line_no )
			line = (LINE_PTR) line->Node.mln_Succ;
	}

	return line;
}


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

/*
 * Text_GetLength()
 *
 * PURPOSE:
 *
 * INPUTS :
 *
 * RETURNS:
 */

LINE_LEN_TYPE
Text_GetNoOfLines( const TEXT_PTR text )
{
	return text->Length;
}



