/*
\\		Draw method for editor class
*/

	#include "Editor.h"
	#include <math.h>

static SAVEDS ULONG expandline( struct ClassData *data, UBYTE *buffer, ULONG maxlen, UBYTE *linestart, ULONG len )
{
	/* Expand tabs in line, and store in buffer */

	ULONG currpos;

	for( currpos = 0; len && (currpos < maxlen); linestart++, len-- )
	{
		currpos++;
		if( *linestart == '\t' )
		{
			*buffer++ = ' ';
			for( ; (currpos < maxlen) && h_tab( data, currpos ); currpos++ )
				*buffer++ = ' ';
		}
		else *buffer++ = *linestart;
	}
	if( currpos < maxlen ) *buffer = ' ';		/* Finish off with a space */
	return( currpos );
}

SAVEDS void draw_internal( struct ClassData *data, struct rendermsg *msg )
{
	/* Default line rendering function (see rendermsg struct for details) */

	LONG markx1, markx2, xindent;

	xindent = (msg->xsize % msg->linelen)>>1;	/* Amount to indent text by (to center) */
	SetFont( msg->rp, msg->tf );

	SetAPen( msg->rp, msg->backcol );
	RectFill( msg->rp, 0, 0, msg->xsize - 1, msg->tf->tf_YSize - 1 );
	SetABPenDrMd( msg->rp, msg->textcol, msg->backcol, JAM1 );
	Move( msg->rp, xindent, msg->tf->tf_Baseline );
	Text( msg->rp, msg->string, min( msg->linelen, msg->strlen ) );	/* Print basic line */

	if( msg->markmin != -1 )												/* Are we marking? */
	{
		SetABPenDrMd( msg->rp, msg->marktextcol, msg->markbackcol, JAM2 );
		if( (msg->markmin < msg->linelen) && (msg->markmax >= 0) )
		{
			markx1 = max( 0, msg->markmin );
			markx2 = min( msg->markmax, msg->linelen - 1 );
			Move( msg->rp, xindent + msg->tf->tf_XSize * markx1, msg->tf->tf_Baseline );
			Text( msg->rp, msg->string + markx1, markx2 - markx1 + 1 );
		}
	}
	if( (msg->curspos >= 0) && (msg->curspos < msg->linelen) )	/* Have we a CURSOR? */
	{
		SetABPenDrMd( msg->rp, msg->curstextcol, msg->cursbackcol, JAM2 );
		Move( msg->rp, xindent + msg->tf->tf_XSize * msg->curspos, msg->tf->tf_Baseline );
		Text( msg->rp, msg->string + msg->curspos, 1 );
	}
}

static SAVEDS void printcurrline( struct ClassData *data, UWORD ypos )
{
	WORD numchars;
	UBYTE buffer[ 1024 ];
	struct rendermsg msg;

	numchars = expandline( data, buffer, 1024, data->linebuffer, data->buffersize );
	msg.string = buffer + data->topvisx;
	msg.strlen = max( 0, numchars - data->topvisx );
	msg.curspos = calcxpos( data, data->linebuffer, data->currx ) - data->topvisx;
	markbounds( data, data->curry, (ULONG *) &msg.markmin, (ULONG *) &msg.markmax );
	if( msg.markmin != -1 )
	{
		msg.markmin = calcxpos( data, data->linebuffer, msg.markmin ) - data->topvisx;
		if( msg.markmax == -1 ) 
			msg.markmax = calcxpos( data, data->linebuffer, data->buffersize ) + 1;
		else msg.markmax = calcxpos( data, data->linebuffer, msg.markmax );
		msg.markmax = min( data->topvisx + data->colsvisible, msg.markmax - data->topvisx - 1 );
	}
	h_print( data, &msg, ypos );
}

static SAVEDS void printline( struct ClassData *data, ULONG line, UWORD ypos )
{
	struct linedata *ld;
	WORD numchars;
	UBYTE buffer[ 1024 ];
	struct rendermsg msg;

	if( ld = findvisloc( data, line ) )
	{
		numchars = expandline( data, buffer, 1024, ld->data, ld->len );
		msg.string = buffer + data->topvisx;
	}
	else numchars = 0;
	msg.strlen = max( 0, numchars - data->topvisx );
	msg.curspos = -1;
	markbounds( data, line, (ULONG *) &msg.markmin, (ULONG *) &msg.markmax );
	if( msg.markmin != -1 )
	{
		msg.markmin = calcxpos( data, ld->data, msg.markmin ) - data->topvisx;
		if( msg.markmax == -1 ) 
			msg.markmax = calcxpos( data, ld->data, ld->len ) + 1;
		else msg.markmax = calcxpos( data, ld->data, msg.markmax );
		msg.markmax = min( data->topvisx + data->colsvisible, msg.markmax - data->topvisx - 1);
	}
	h_print( data, &msg, ypos );
}

SAVEDS UWORD fillborders( struct ClassData *data )
{
	UWORD excess;

	excess = (_mheight(data->obj) % data->font->tf_YSize)/2;
	SetAPen( _rp(data->obj), data->backcol );
	RectFill( _rp(data->obj), _mleft(data->obj), _mtop(data->obj), _mright(data->obj), _mtop(data->obj)+excess );
	RectFill( _rp(data->obj), _mleft(data->obj), _mbottom(data->obj)-excess, _mright(data->obj), _mbottom(data->obj) );

	return( (UWORD) (_mtop(data->obj) + excess) );
}

static SAVEDS void drawobject( struct ClassData *data, ULONG minline, ULONG maxline )
{
	UWORD ypos;
	ULONG currline;
	
	ypos = _mtop(data->obj) + (_mheight(data->obj) % data->font->tf_YSize)/2;
	currline = data->topvisy;

	for( ; ypos <= (_mbottom(data->obj) - data->font->tf_YSize + 1); ypos += data->font->tf_YSize, currline++ )
	{
		if( (currline >= minline) && (currline <= maxline ) )
		{
			if( currline == data->curry )
				printcurrline( data, ypos );
			else printline( data, currline, ypos );
		}
	}
}

static SAVEDS void drawupdate( struct ClassData *data )
{
	UWORD bottom, starty, endy;
	LONG diff, height;

	if( (data->flags & DRAWALL) )		/* Print everything... */
	{
		data->flags &= ~(DRAWLINE|DRAWALL|DRAWSCROLL|DRAWINSERT|DRAWREMOVE);
		drawobject( data, data->topvisy, data->topvisy+data->linesvisible-1 );
		data->oldvisy = data->topvisy;
		data->oldy = data->curry;
	}
	else {
		if(	((data->flags & DRAWINSERT) && (data->flags & DRAWREMOVE)) &&
				(data->flags & (DRAWSCROLL|DRAWLINE)) )
		{
			data->flags |= DRAWALL;		/* The flags are conflicting... */
			drawupdate( data );	/* ... so we'll do a complete redraw */
		}
		starty	= _mtop(data->obj) + (_mheight(data->obj)%data->font->tf_YSize)/2;
		endy		= _mbottom(data->obj) - (_mheight(data->obj)%data->font->tf_YSize)/2 - 1;
		bottom	= data->topvisy + data->linesvisible - 1;

		if( data->flags & DRAWSCROLL )	/* Move area up/down, redraw */
		{
			data->flags &= ~DRAWSCROLL;
			diff = max( min( data->topvisy - data->oldvisy, data->linesvisible), -data->linesvisible);
			if( abs(diff) > (data->linesvisible/2) )	/* To much to really scroll? */
			{
				data->flags |= DRAWALL;
				drawupdate( data );
			}
			else {
				height = data->font->tf_YSize*diff;
				SetBPen( _rp(data->obj), data->backcol );
				if( diff ) ScrollRaster( _rp(data->obj), 0, height, _mleft(data->obj), starty, _mright(data->obj), endy );
				if( diff > 0 )
					drawobject( data, bottom-diff, bottom );
				else if( diff < 0 )
					drawobject( data, data->topvisy, data->topvisy-diff );
				data->oldvisy = data->topvisy;
				data->flags |= DRAWLINE;
			}
		}
		if(	(data->flags & DRAWINSERT)	/* Move area down, redraw ypos, ypos+1  */
				&& (data->curry >= data->topvisy) && (data->curry <= bottom) )
		{
			starty += data->font->tf_YSize * (data->curry-data->topvisy);
			SetBPen( _rp(data->obj), data->backcol );
			ScrollRaster( _rp(data->obj), 0, -data->font->tf_YSize, _mleft(data->obj), starty, _mright(data->obj), endy );
			drawobject( data, data->curry-1, data->curry );
			data->flags &= ~DRAWINSERT;
		}
		else if(	(data->flags & DRAWREMOVE)	/* Move area up, redraw bottom visible */
					&& (data->curry >= data->topvisy) && (data->curry <= bottom) )
		{
			starty += data->font->tf_YSize * (1+data->curry-data->topvisy);
			SetBPen( _rp(data->obj), data->backcol );
			ScrollRaster( _rp(data->obj), 0, data->font->tf_YSize, _mleft(data->obj), starty, _mright(data->obj), endy );
			drawobject( data, data->curry, data->curry );
			drawobject( data, bottom, bottom );
			data->flags &= ~DRAWREMOVE;
		}
		if( data->flags & DRAWMARKED )	/* Print from oldy to curry */
		{
			data->flags &= ~(DRAWMARKED|DRAWLINE);
			drawobject( data, min( data->curry, data->oldy ), max( data->curry, data->oldy ) );
			data->oldy = data->curry;
		}
		if( data->flags & DRAWLINE )
		{
			data->flags &= ~DRAWLINE;
			if( (data->oldy != data->curry) && (data->oldy >= data->topvisy) && (data->oldy <= bottom) )
				printline( data, data->oldy, starty + (data->oldy - data->topvisy) * data->font->tf_YSize );
			if( (data->curry >= data->topvisy) && (data->curry <= bottom) )
				printcurrline( data, starty + (data->curry - data->topvisy) * data->font->tf_YSize );
			data->oldy = data->curry;
		}
	}
}

SAVEDS ULONG m_draw( struct IClass *cl, Object *obj, struct MUIP_Draw *msg )
{
	struct ClassData *data = INST_DATA( cl, obj );
	ULONG linesavail;
	UBYTE drawmode;

	DoSuperMethodA( cl, obj, (APTR) msg );

	if( !data->quietcount )				/* Can we redraw right now? */
	{
		drawmode = _rp(obj)->DrawMode;

		linesavail = _mheight(obj)/data->font->tf_YSize;
		if( linesavail != data->linesvisible )
			set( obj, MUIA_Editor_VisLines, linesavail );
		linesavail = _mwidth(obj)/data->font->tf_XSize;
		if( linesavail != data->colsvisible )
			set( obj, MUIA_Editor_VisCols, linesavail );

		if( data->rp.BitMap && data->linesvisible && data->colsvisible )
		{
			if( msg->flags & MADF_DRAWOBJECT )
			{
				fillborders( data );		/* Fill in any unused border areas */
				drawobject( data, data->topvisy, data->topvisy+data->linesvisible-1 ); /* Complete redraw */
			}
			if( msg->flags & MADF_DRAWUPDATE )
			{
				drawupdate( data );			/* Selective refresh */
			}
		}

		data->topdrawny = data->topvisy;		/* Keep track of the rendered pos */
		_rp(obj)->DrawMode = drawmode;
	}
	return( 0 );
}
