/*
\\		Input handler for Editor Class
*/

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

static SAVEDS void insert( struct ClassData *data, UBYTE c, UWORD pos )
{
	UBYTE *src, *dst;
	ULONG count;

	src = data->linebuffer + data->buffersize - 1;
	dst = src + 1;

	if( data->buffersize < 1023 )		/* Is there room for this? */
	{
		for( count = data->buffersize - pos; count; count-- )
			*dst-- = *src--;
		*(data->linebuffer + pos) = c;
		data->buffersize++;
	}
}

static SAVEDS void rem( struct ClassData *data, UWORD len, UWORD pos )
{
	UBYTE *src, *dst;

	dst = data->linebuffer + pos;
	src = dst + len;

	if( (pos < 1024 ) && src <= (data->linebuffer+data->buffersize) )
	{
		for( ; src < (data->linebuffer + data->buffersize); )
			*dst++ = *src++;
		data->buffersize -= len;
	}
}

static SAVEDS LONG prevword( struct ClassData *data )
{
	LONG curr;

	curr = data->currx - 1;

	if( curr < 0 ) return( -1 );
	for( ; (curr >= 0) && !isalnum(data->linebuffer[curr]); curr-- );
	if( curr < 0 ) return( -1 );
	for( ; (curr >= 0) && isalnum(data->linebuffer[curr]); curr-- );
	return( curr+1 );
}
static SAVEDS LONG nextword( struct ClassData *data )
{
	LONG curr;

	for( curr = data->currx; (curr<data->buffersize) && isalnum(data->linebuffer[curr]); curr++ );
	if( curr >= data->buffersize ) return( NULL );
	for( ; (curr < data->buffersize) && !isalnum(data->linebuffer[curr]); curr++ );
	if( curr >= data->buffersize ) return( NULL );
	return( curr );
}

static SAVEDS void button( struct ClassData *data, Object *obj, ULONG message )
{
	/* Process key input events */

	ULONG newpos;
	UBYTE c;

	if( (message & MSG_TYPE) == MSG_SPECIAL ) switch( message & MSG_CODE )
	{
		case SPC_NOP:		break;
		case SPC_EOL:		set( obj, MUIA_Editor_CurrX, MUIV_Editor_Max );
								break;
		case SPC_SOL:		set( obj, MUIA_Editor_CurrX, MUIV_Editor_Min );
								break;
		case SPC_DEOL:		data->buffersize = data->currx;
								data->flags |= CHANGED|DRAWLINE;
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_DSOL:		if( data->currx )
								{
									rem( data, data->currx, 0 );
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Min );
									data->flags |= CHANGED|DRAWLINE;
									MUI_Redraw( obj, MADF_DRAWUPDATE );
								}
								break;
		case SPC_EOF:		set( obj, MUIA_Editor_CurrY, MUIV_Editor_Max );
								break;
		case SPC_SOF:		set( obj, MUIA_Editor_CurrY, MUIV_Editor_Min );
								break;
		case SPC_NW:		if( newpos = nextword( data ) )
									set( obj, MUIA_Editor_CurrX, newpos );
								else if( data->currx < data->buffersize )
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Max );
								else if( data->curry < (data->lines-1) )
								{
									data->quietcount++;
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Next );
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Min );
									data->quietcount--;
								}
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_PW:		if( (newpos = prevword( data )) != -1 )
									set( obj, MUIA_Editor_CurrX, newpos );
								else if( data->currx > 0 )
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Min );
								else if( data->curry > 0 )
								{
									data->quietcount++;
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Prev );
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Max );
									data->quietcount--;
								}
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_NC:		if( (data->currx < data->buffersize)  )
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Next );
								else if( data->curry < (data->lines-1) )
								{
									data->quietcount++;
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Next );
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Min );
									data->quietcount--;
								}
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_PC:		if( data->currx > 0 )
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Prev );
								else if( data->curry > 0 )
								{
									data->quietcount++;
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Prev );
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Max );
									data->quietcount--;
								}
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_DPW:		if( (newpos = prevword( data )) != -1 )
								{
									rem( data, data->currx - newpos, newpos );
									set( obj, MUIA_Editor_CurrX, newpos );
								}
								else if( data->currx > 0 )
								{
									rem( data, data->currx, 0 );
									set( obj, MUIA_Editor_CurrX, 0 );
								}
								else break;
								data->flags |= CHANGED|DRAWLINE;
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_DNW:		if( newpos = nextword( data ) )
									rem( data, newpos - data->currx, data->currx );
								else if( data->currx < data->buffersize )
									rem( data, data->buffersize-data->currx, data->currx );
								else break;
								data->flags |= CHANGED|DRAWLINE;
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_DNC:		if(	(data->currx == data->buffersize) &&
										(data->lines > 1) &&
										(data->curry < (data->lines-1))) /* Have we deleted the newline? */
								{
									data->quietcount++;
									storebuffer( data );
									mergelines( data, data->curry );
									getbuffer( data );
									data->flags |= CHANGED|DRAWREMOVE;
									data->quietcount--;
								}
								else {				/* Just a normal char */
									data->flags |= CHANGED|DRAWLINE;
									rem( data, 1, data->currx );
								}
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_DPC:		if( data->currx > 0 )
								{
									rem( data, 1, data->currx-1 );
									set( obj, MUIA_Editor_CurrX, MUIV_Editor_Prev );
									data->flags |= DRAWLINE;
								}
								else if( data->curry > 0 )
								{
									data->quietcount++;
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Prev );
									set( obj, MUIA_Editor_CurrX, data->currline->len );
									mergelines( data, data->curry );
									data->flags &= ~DRAWLINE;
									getbuffer( data );
									data->flags |= DRAWREMOVE;
									data->quietcount--;
								}
								data->flags |= CHANGED;
								MUI_Redraw( obj, MADF_DRAWUPDATE );
								break;
		case SPC_PL:		if( data->curry > 0 )
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Prev );
								break;
		case SPC_NL:		if( data->curry < (data->lines-1) )
									set( obj, MUIA_Editor_CurrY, MUIV_Editor_Next );
								break;
		case SPC_PP:		if( data->curry > 0 )
								{
									newpos = (data->linesvisible>1)?(data->linesvisible-1):1;
									set( obj, MUIA_Editor_CurrY, (data->curry>=newpos)?(data->curry-newpos):0 );
								}
								break;
		case SPC_NP:		set( obj, MUIA_Editor_CurrY, min( data->lines-1, data->curry + data->linesvisible - 1) );
								break;
		case SPC_NOBJ:		set( _win(obj), MUIA_Window_ActiveObject, MUIV_Window_ActiveObject_Next );
								break;
		case SPC_POBJ:		set( _win(obj), MUIA_Window_ActiveObject, MUIV_Window_ActiveObject_Prev );
								break;
	}
	else if( (message & MSG_TYPE) == MSG_CHAR )
	{
		if( data->flags & ISEMPTY ) set( obj, MUIA_Editor_IsEmpty, FALSE );
		c = message & MSG_CODE;
		data->flags |= CHANGED;
		if( c != '\n' ) {
			insert( data, c, data->currx );
			set( obj, MUIA_Editor_CurrX, MUIV_Editor_Next );
			data->flags |= DRAWLINE;
		}
		else {
			insert( data, c, data->currx );
			storebuffer( data );
			getbuffer( data );
			data->quietcount++;
			set( obj, MUIA_Editor_CurrY, MUIV_Editor_Next );
			set( obj, MUIA_Editor_CurrX, (data->flags&AUTOINDENT)?indent_size(data):MUIV_Editor_Min );
			data->flags &= ~DRAWLINE;
			data->flags |= DRAWINSERT;
			data->quietcount--;
		}
		MUI_Redraw( obj, MADF_DRAWUPDATE );
	}
	if( !(data->flags&ISEMPTY) && (data->lines==1) && (data->buffersize==0) )
		set( obj, MUIA_Editor_IsEmpty, TRUE );
}

static SAVEDS void mouse( struct ClassData *data, Object *obj, struct IntuiMessage *imsg )
{
	/* Process Mouse click/doubleclick */

	WORD x, y;
	ULONG activeobject;

	if(	((imsg->MouseX >= _mleft(obj)) &&						/* Is it in our object? */
			(imsg->MouseX <= _mright(obj)) &&
			(imsg->MouseY >= _mtop(obj)) &&
			(imsg->MouseY <= _mbottom(obj))) ||
			(data->flags & DRAGGING) )
		{
			if( imsg->Code == IECODE_LBUTTON )
			{
				MUI_RequestIDCMP( obj, IDCMP_MOUSEMOVE|IDCMP_INTUITICKS );
				data->flags |= DRAGGING;
			}
			if(	(imsg->Code == (IECODE_LBUTTON|IECODE_UP_PREFIX)) ||
					(imsg->Code == IECODE_RBUTTON) )
			{
				MUI_RejectIDCMP( obj, IDCMP_MOUSEMOVE|IDCMP_INTUITICKS );
				data->flags &= ~DRAGGING;
			}

			get( _win(obj), MUIA_Window_ActiveObject, &activeobject );
			if( activeobject != (ULONG) obj )			/* Mouse click - not active object?! */
				set( _win(obj), MUIA_Window_ActiveObject, obj );

			storebuffer( data );
			y = data->topvisy + (imsg->MouseY-_mtop(obj)-(_mheight(obj) % data->font->tf_YSize)/2) / data->font->tf_YSize;
			if( y < 0 ) y = 0;
			x = max( 0, data->topvisx + (imsg->MouseX-_mleft(obj))/data->font->tf_XSize);
			if( imsg->Code == IECODE_LBUTTON ) set( obj, MUIA_Editor_ClickColumn, x );
			x = correctxpos( data, findvisloc( data, (y<data->lines)?y:(data->lines-1) )->data, x );
			if( (x == data->currx) && (y == data->curry))
			{
				if(	(imsg->Code == IECODE_LBUTTON) &&
						DoubleClick( data->doublesecs, data->doublemicros, imsg->Seconds, imsg->Micros ) )
				{
					if( !(data->flags & MARKING) )	/* Are we already marking? */
						SetAttrs( obj,	MUIA_Editor_Quiet, TRUE,
											MUIA_Editor_Marking, TRUE,
											MUIA_Editor_MarkX, x,
											MUIA_Editor_MarkY, y,
											MUIA_Editor_Quiet, FALSE,
											TAG_DONE );
					else set( obj, MUIA_Editor_Marking, FALSE );
				}
			}
			else {
				if( data->flags & MARKING ) data->flags |= (y!=data->curry)?DRAWMARKED:DRAWLINE;
				SetAttrs( obj,	MUIA_Editor_Quiet, TRUE,
									MUIA_Editor_CurrY, (y<data->lines)?y:(data->lines-1),
									MUIA_Editor_CurrX, x,
									MUIA_Editor_Quiet, FALSE,
									TAG_DONE );
			}
			if( imsg->Code == IECODE_LBUTTON )
			{
				data->doublesecs = imsg->Seconds;
				data->doublemicros = imsg->Micros;
			}
		}
}

SAVEDS ULONG m_handleinput(	struct IClass *cl,
										Object *obj,
										struct MUIP_HandleInput *msg )
{
	/* Handle editor input, update all flags, and redraw when necessary */

	struct ClassData *data = INST_DATA( cl, obj );
	ULONG actobj, defobj, userbutton;

	if( msg->imsg->Class & (IDCMP_RAWKEY|IDCMP_VANILLAKEY) )
	{
		get( _win(obj), MUIA_Window_ActiveObject, &actobj );
		get( _win(obj), MUIA_Window_DefaultObject, &defobj );
		if(	(actobj == (ULONG) obj) ||
				((actobj == NULL) && (defobj == (ULONG) obj)) )
		{
			userbutton = h_input( data, msg );
			button( data, obj, userbutton );
		}
		else return( DoSuperMethodA( cl, obj, msg ) );	/* Not the active object */
	}

	if( msg->imsg->Class & (IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE|IDCMP_INTUITICKS) )
	{
		mouse( data, obj, msg->imsg );			/* Process mousebutton */
	}

	return( 0 );
}
