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

    /* is the list full? */
    if( text->Length < MAX_TEXT_LEN )
    {
        /* nope, still room */

        LINE_LEN_TYPE   len = line_end - line_start;

        if( new_line = Line_New( line_start, len, err_code ) )
        {
            AddTail( (struct List *) &(text->Contents), (struct Node *) &(new_line->Node) );
            text->Length++;
            if( len > text->MaxWidth ) text->MaxWidth = len;
        }
    }
    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:
 */

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


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

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

LINE_LEN_TYPE
Text_GetMaxWidth( const TEXT_PTR text )
{
    return text->MaxWidth;
}


