#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include <devices/inputevent.h>
#include <exec/memory.h>
#include <exec/semaphores.h>
#include <intuition/intuition.h>
#include <intuition/sghooks.h>

#include <clib/exec_protos.h>
#include <clib/utility_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/utility_pragmas.h>

extern ULONG read_clip( UBYTE *, ULONG );
extern void write_clip( UBYTE *, ULONG );
extern char *IncrementNum( char *target, char *string );
extern LONG next_word( UBYTE *buffer, LONG pos, LONG max );
extern LONG prev_word( UBYTE *buffer, LONG pos );
extern void ToggleCase( UBYTE *character );

#define IECODE_UNUSED 0
#define IECODE_C 51
#define IECODE_G 36
#define IECODE_V 52
#define IECODE_I 23
#define IECODE_RETURN 68
#define IECODE_ESC 69
#define IECODE_BACKSP 65
#define IECODE_DEL 70
#define IECODE_CRSRUP 76
#define IECODE_CRSRDWN 77
#define IECODE_CRSRRIGHT 78
#define IECODE_CRSRLEFT 79
#define IECODE_HELP 95
#ifndef SGA_NEXTACTIVE
	#define SGA_NEXTACTIVE (0x20L) /* Make next possible gadget active */
	#define SGA_PREVACTIVE (0x40L) /* Make previous possible gadget active */
#endif

extern struct Library *SysBase, *DOSBase, *UtilityBase;
extern struct Hook *OldHook;
extern char *SemaphoreName;
extern struct SignalSemaphore *Sem;
extern BOOL Disabled;

/****** string_hook() *******************************************************
*
*   NAME
*        string_hook -- general EditHook for Gadtools Stringgadgets
*   SYNOPSIS
*        Known = string_hook ( Hook, Object, Message )
*        D0                    A0    A2      A1
*        ULONG   string_hook ( struct Hook *, struct SGWork *, APTR * );
*   FUNCTION
*        Normally set as new global Hook with SetEditHook().
*        The following functions will be supported:
*        SHIFT CURSOR RIGHT  next word
*        SHIFT CURSOR LEFT   previous word
*        ALT BACKSPACE       delete previous word
*        ALT DEL             delete next word
*        RALT CURSOR UP      go to prev gadget (with GADGETUP)
*        RALT CURSOR DOWN    go to next gadget (with GADGETUP)
*        ESC                 leave gadget (with GADGETUP)
*        RCommand C          copy gadget contents to clipboard 0
*        RCommand V          paste contents of clip 0 to gadget
*        sgw->EditOp will be set to EO_BIGCHANGE, if some text has been
*        deleted or inserted from the clipboard.
*
*  Added by Paul Huxham, 4th February 1994:
*        RCommand I          Increments a numbered filename by one
*        RALT I              Increments a numbered filename by one
*
*  Added by Paul Huxham, 29th May 1994:
*        RALT C              copy gadget contents to clipboard 0
*        RALT V              paste contents of clip 0 to gadget
*        RALT G              toggle the case of selected character
*        RCommand G          toggle the case of selected character
*        SHIFT RALT G        toggle the case of characters until EOL or
*                            non alphabetic character is found
*        SHIFT RCommand G    toggle the case of characters until EOL or
*                            non alphabetic character is found
*
*        sgw->IEvent->ie_Code will be modified to 0x00 if we moved to the
*        next/previous word/gadget or if AMIGA-C or AMIGA-V was pressed. The
*        keycode 0 is unused by the system and therefore will be ignored by
*        further stringhooks.
*
*   INPUTS
*        Hook   - pointer to own hook-structure
*        Object - struct SGWork for Stringgadgets
*        Message- ???
*
*   RESULT
*        ~0 - command supported, else 0
*        sgw->EditOp and sgw->IEvent->ie_Code may be motified (see above)
*
***************************************************************************/
ULONG __saveds __asm  string_hook( register __a0 struct Hook   *hook,
                                   register __a2 struct SGWork *sgw,
                                   register __a1 unsigned long *msg  )
{
	struct InputEvent *ie;
	ULONG length;
	ULONG return_code = ~0;

	ObtainSemaphoreShared( Sem );

	if ( *msg == SGH_KEY )
	{
		/* If Commodities says we are enabled */
		if ( !Disabled )
		{
			ie = sgw->IEvent;

			if ( ie->ie_Class == IECLASS_RAWKEY )
			{
				switch ( ie->ie_Code )
				{
					case IECODE_ESC:
						sgw->Actions |= SGA_END;
					break;

					case IECODE_CRSRUP:
						if ( ie->ie_Qualifier & IEQUALIFIER_RALT )
						{
							sgw->Actions |= SGA_PREVACTIVE | SGA_END;
							ie->ie_Code = IECODE_UNUSED;
						}
					break;

					case IECODE_CRSRDWN:
						if ( ie->ie_Qualifier & IEQUALIFIER_RALT )
						{
							sgw->Actions |= SGA_NEXTACTIVE | SGA_END;
							ie->ie_Code = IECODE_UNUSED;
						}
					break;

					case IECODE_CRSRRIGHT:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_LALT | IEQUALIFIER_RALT ) )
						{
							sgw->BufferPos = next_word( sgw->WorkBuffer, sgw->BufferPos, sgw->StringInfo->NumChars );
							ie->ie_Code = IECODE_UNUSED;
							sgw->Actions |= SGA_REDISPLAY;
						}
					break;

					case IECODE_CRSRLEFT:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_LALT | IEQUALIFIER_RALT ) )
						{
  						sgw->BufferPos= prev_word( sgw->WorkBuffer, sgw->BufferPos );
							ie->ie_Code = IECODE_UNUSED;
							sgw->Actions |= SGA_REDISPLAY;
						}
					break;

					case IECODE_BACKSP:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_LALT | IEQUALIFIER_RALT ) )
						{
  						sgw->BufferPos = prev_word( sgw->WorkBuffer, sgw->BufferPos ) + 1;

							if ( sgw->BufferPos < sgw->StringInfo->BufferPos )
								strcpy( &sgw->WorkBuffer[ sgw->BufferPos ], &sgw->WorkBuffer[ sgw->StringInfo->BufferPos ] );

							sgw->Actions |= SGA_REDISPLAY;
							sgw->EditOp = EO_BIGCHANGE;
						}
					break;

					case IECODE_DEL:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_LALT | IEQUALIFIER_RALT ) )
						{
							length = next_word( sgw->WorkBuffer, sgw->BufferPos, sgw->StringInfo->NumChars );
							strcpy ( &sgw->WorkBuffer[ sgw->BufferPos + 1 ], &sgw->WorkBuffer[ length ] );

							sgw->Actions |= SGA_REDISPLAY;
							sgw->EditOp = EO_BIGCHANGE;
						}
					break;

					case IECODE_C:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_RALT | IEQUALIFIER_RCOMMAND ) )
						{
							write_clip( sgw->StringInfo->Buffer, sgw->NumChars );
							sgw->Code = IECODE_UNUSED;
						}
					break;

					case IECODE_G:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_RALT | IEQUALIFIER_RCOMMAND ) )
						{
							if ( ie->ie_Qualifier & ( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT ) )
							{
								while( isalpha( sgw->WorkBuffer[ sgw->BufferPos ] ) != 0 )
								{
	  							ToggleCase( &sgw->WorkBuffer[ sgw->BufferPos ] );
									sgw->BufferPos++;
								}

								sgw->Actions |= SGA_REDISPLAY;
								sgw->Code = IECODE_UNUSED;
							}
							else
							{
	  						ToggleCase( &sgw->WorkBuffer[ sgw->BufferPos ] );
								sgw->BufferPos++;

								sgw->Actions |= SGA_REDISPLAY;
								sgw->Code = IECODE_UNUSED;
							}
						}
					break;

					case IECODE_I:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_RALT | IEQUALIFIER_RCOMMAND ) )
						{
							char *newstr;

							newstr = AllocMem( 200, MEMF_PUBLIC | MEMF_CLEAR );
							if ( newstr != NULL )
							{
								if ( IncrementNum( newstr, sgw->StringInfo->Buffer ) != NULL )
								{
									strcpy( sgw->WorkBuffer, newstr );

									sgw->BufferPos = strlen( newstr );
									sgw->NumChars = strlen( newstr );
								}
								else sgw->Actions |= SGA_BEEP;

								sgw->EditOp = EO_BIGCHANGE;
								sgw->Code = IECODE_UNUSED;

								FreeMem( newstr, 200 );
							}
						}
					break;

					case IECODE_V:
						if ( ie->ie_Qualifier & ( IEQUALIFIER_RALT | IEQUALIFIER_RCOMMAND ) )
						{
							WORD maxchars, pos;

							maxchars = sgw->StringInfo->MaxChars - 1;
							pos = sgw->NumChars;

							while ( --pos >= sgw->BufferPos )
								sgw->WorkBuffer[ maxchars - sgw->NumChars + pos ] = sgw->WorkBuffer[ pos ];

							length = read_clip( &sgw->WorkBuffer[ sgw->BufferPos ], maxchars - sgw->NumChars );

							if ( length > 0 )
							{
								strcpy( &sgw->WorkBuffer[ sgw->BufferPos + length ],
									&sgw->WorkBuffer[ maxchars - sgw->NumChars + sgw->BufferPos ] );

								sgw->BufferPos += length;
								sgw->NumChars += length;
							}
							else sgw->Actions |= SGA_BEEP;

							sgw->EditOp = EO_BIGCHANGE;
							sgw->Code = IECODE_UNUSED;
						}
					break;
				}
			}
		}
		CallHookPkt( OldHook, (APTR)sgw, msg );
	}
	else return_code = 0;

	ReleaseSemaphore( Sem );

	return return_code;
}
