
// aptassist.c

// Lee Wilmot 2008

// Public Domain, Share and Prosper (And Give Credit)

// See also: aptassist.html

#include "resident.h"
#include "share.h"

#include <Libraries/PalmOSGlue/CtlGlue.h>
#include <Libraries/PalmOSGlue/FrmGlue.h>

#include <Standalone.h>
STANDALONE_CODE_RESOURCE_ID(1000);

void handleRotate( actionType action ) ;
void actionOrientation( UInt16 desired );

void flashBoxNum( const char *text, Int32 num, Int32 ms );
Boolean resourceToBuffer( char **buffer, char *name, UInt16 resID );
void mySwitchToPinlet( actionType action, actionType actionMode );
void handleFormObject( actionType action, UInt16 resourceID, Int16 amountOrAction );
void showResourceIDs(  actionType action );
void simHardButton( actionType action, UInt32 numberOfTimes );
void actionKhromaTheme( char *themeName ) ;
void setColours( RGBColorType* c, UInt16 count );

void DAMain( void )
{
	actionType action = fget( FtrAptAssistAction );
	UInt32 actionArg = fget( FtrAptAssistArg );

	if ( isHardSimAction( action ) )
		simHardButton( action, actionArg );
	else if ( 
		( action >= actionTypePinModeFirst ) &&
		( action <= actionTypePinModeLast )
	)
		mySwitchToPinlet( NULL, action );		
	else if (
			 ( action >= actionTypeFirstPinAction ) &&
			 ( action <= actionTypeLastPinAction )
        )
		mySwitchToPinlet( action, 0 );
	else if(
			 ( 
			  ( action >= actionCatFieldFirst ) &&
			  ( action <= actionCatFieldLast )
			  ) ||
			 ( action == actionTypeSimControl ) ||
			 ( action == actionTypeSimControlEnable ) ||
			 ( action == actionTypeSimControlDisable ) 
	 )
		 handleFormObject( action, actionArg, 0 );
	else if ( action == actionTypeSimScrollUp )
		handleFormObject( action, actionArg, -10 );
	else if ( action == actionTypeSimScrollDown )
		handleFormObject( action, actionArg, +10 );
	else if ( 
			 ( action >= actionTypeShowListFirst ) &&
			 ( action < ( actionTypeShowListFirst + 4 ) )
	 )
		showResourceIDs( action );
	else if ( 
			 ( action == actionTypeRotateLeft ) ||
			 ( action == actionTypeRotateRight )
	 ) 
		handleRotate( action );
	else if ( 
			 ( action >= actionTypeRotateSetUser ) &&
			 ( action <= actionTypeRotateLast )
	 )
		actionOrientation( action - actionTypeRotateSetUser );

	// undocumented, not Apt actions

	else if ( action == assistActionSetKhromaTheme )
		actionKhromaTheme( (char *) actionArg );
	else
		flashBox( "AptAssist: unknown action, old version ? ", 1500 );
}

void mySwitchToPinlet( actionType action, actionType modeAction )
{
	if (  getPINVersion() > 0 ) {
		UInt16 refNum;
		
		const char pinIDs[3][10] = {
			pinClassic, 
			pinTriCell,
			pinStdKeyboard
		};

		// Shouldn't we do a find first ? Is it one of those that always needs a load ?

		if ( SysLibLoad( sysFileTLibrary, 'pinM', &refNum ) == errNone ) {

			if ( PinLibOpen( refNum ) == errNone ) {

				const char *currID = PinGetCurrentPinletID( refNum );
				UInt16 currMode =  PinGetInputMode( refNum );
				
				UInt16 modeToSet;
				const char *idToSet;

				if ( modeAction == 0 )
					modeToSet = currMode;
				else
					modeToSet = modeAction -  actionTypePinModeFirst;

				if ( action == NULL )
					idToSet = currID;
				else
					idToSet = pinIDs[action - actionTypeFirstPinAction];

				// Detection of current mode doesn't always work...

				PinSwitchToPinlet( refNum,  idToSet, modeToSet );
				PinSetInputMode( refNum, modeToSet );

				PinLibClose( refNum );
			}

			SysLibRemove( refNum );
		}
	}
}

void handleFormObject( actionType action, UInt16 resourceID, Int16 amountOrAction )
{
	FormPtr formP = FrmGetActiveForm();
	UInt16 objIndex = frmInvalidObjectId;

	FormObjectKind reqKind;

	if ( ! formP ) 
		return;

	switch ( action ) {
	case actionTypeSimControl:
	case actionTypeSimControlEnable:
	case actionTypeSimControlDisable:
		reqKind = frmControlObj;
		//		flashBox( "gadg", 1000 );
		//		reqKind = frmGadgetObj;
		break;
	case actionTypeSimScrollUp:
	case actionTypeSimScrollDown:
		reqKind = frmScrollBarObj;
		break;
	default:
		reqKind = frmFieldObj;
		break;
	}

	// None specified, get one with focus

	if ( resourceID == 0 ) {
		UInt32 focusIndex = FrmGetFocus( formP );
		if ( focusIndex != noFocus ) {
			FormObjectKind kind = FrmGetObjectType( formP, focusIndex );
			if ( kind == reqKind )
				resourceID = FrmGetObjectId( formP, focusIndex );
		}
	}
	
	// Not of right type: try first in form
	if ( resourceID == 0 ) {
		
		UInt16 noObjects = FrmGetNumberOfObjects( formP );
		UInt16 objectNo;
		
		for ( objectNo = 0; objectNo < noObjects; objectNo++ ) {
			if ( FrmGetObjectType( formP, objectNo ) == reqKind ) {
				objIndex = objectNo;
				resourceID = FrmGetObjectId( formP, objIndex );
				break;
			}
		}
		if ( objIndex == frmInvalidObjectId ) {
			errFlash( "no object found");
			return;
		}
	}
	else 
		objIndex = FrmGetObjectIndex( formP, resourceID );

	if ( objIndex == frmInvalidObjectId )
		flashBoxNum( "AptAssist: ignore bad obj ID: ", resourceID, 800 );
	else {
		FormObjectKind kind = FrmGetObjectType( formP, objIndex );
		void *ptr = FrmGetObjectPtr ( formP, objIndex ); 

		if ( kind != reqKind ) 
			flashBoxNum( "AptAssist: ID wrong resource: ", resourceID, 800 );
		else if ( reqKind == frmControlObj ) {
			ControlPtr controlP= (ControlPtr) ptr;
			ControlStyleType style = CtlGlueGetControlStyle( controlP );
			
			switch ( style ) {
				
			case pushButtonCtl:
			case checkboxCtl:
				{
					Int16 val = CtlGetValue ( controlP );
					
					switch ( action ) {
					case actionTypeSimControl:
						val = 1 - val;
						break;
					case actionTypeSimControlEnable:
						val = 1;
						break;
					case actionTypeSimControlDisable:
						val = 0;
						break;
					default:
						break;
					}
					
					if ( CtlGetValue( controlP ) != val ) {
						CtlSetValue( controlP, val );
						CtlHitControl( controlP );
						CtlDrawControl( controlP );
					}
				}
				break;
			
			case popupTriggerCtl:
			case selectorTriggerCtl:
			case buttonCtl:
			case repeatingButtonCtl: 
				if ( action == actionTypeSimControl  )
					CtlHitControl( controlP );
				break;
			default:   
				break;
			}
		}
		else if ( reqKind == frmFieldObj ) {
			FieldPtr fldP= (FieldPtr) FrmGetObjectPtr ( formP, objIndex );
			switch ( action ) {
				// Next version
			case actionTypeFieldToggleEditable:
			case actionTypeFieldToggleUnderlined:
				{
					FieldAttrType attrib;
					FldGetAttributes( fldP, &attrib );
					if ( action == actionTypeFieldToggleEditable ) {
						attrib.editable = ! ( attrib.editable );
						
					}
					else
						attrib.underlined = ! ( attrib.underlined );
					FldSetAttributes( fldP, &attrib );
				}
			case actionTypeFieldExit:
				FldReleaseFocus( fldP );
				break;
			case actionTypeFieldEnter:
				FldGrabFocus( fldP );
				FldDrawField( fldP );
			case actionTypeFieldEnd:		
				FldSetInsertionPoint( fldP, FldGetTextLength( fldP ) );
				break;
			case actionTypeFieldStart:		
				FldSetInsertionPoint( fldP, 0 );
				break;
			case actionTypeFieldCopy:		
				FldCopy( fldP );
				break;
			case actionTypeFieldCut:		
				FldCut( fldP );
				break;
			case actionTypeFieldPaste:		
				FldPaste( fldP );
				break;
			case actionTypeFieldUndo:		
				FldUndo( fldP );
				break;
			case actionTypeFieldSelectAll:
				FldSetSelection( fldP, 0, FldGetTextLength( fldP ) );
				break;
			default:
				break;
			}
		}
		/*		else if ( reqKind == frmGadgetObj ) {
			EventType ev = initEvent( frmGadgetEnterEvent );
			RectangleType  bounds;

			FrmGetObjectBounds( FrmGetActiveForm(), objIndex, &bounds );

			ev.data.gadgetEnter.gadgetID = resourceID;
			ev.data.gadgetEnter.gadgetP =  ( struct FormGadgetType * ) ptr;

			ev.screenX = bounds.topLeft.x + 73;
			ev.screenY = bounds.topLeft.y + 183;
			ev.tapCount = 1;
			ev.penDown = true;

			// enter->
			//			flashBox( "add", 1000 );

			//			if ( resourceID != 1001 )
			//				flashBox( "bad no", 1000 );

			EvtAddEventToQueue( &ev );

			}*/

		else if ( reqKind == frmScrollBarObj ) {
			ScrollBarType *scrollP = (ScrollBarType *) ptr;
			Int16 value, min, max, pageSize;

			EventType ev = initEvent( sclEnterEvent );
				Int16 newValue;

				SclGetScrollBar ( scrollP, &value, &min, &max, &pageSize  );

				if ( amountOrAction > 0 )
					newValue = value + ( pageSize / 2 );
				else
					newValue = value - ( pageSize / 2 );
				
				if ( newValue < min )
					newValue = min;
				if ( newValue > max )
					newValue = max;

				ev.data.sclEnter.scrollBarID = resourceID;
				ev.data.sclEnter.pScrollBar = scrollP;
				EvtAddEventToQueue( &ev );

				ev =initEvent( sclRepeatEvent );
				ev.data.sclRepeat.scrollBarID = resourceID;
				ev.data.sclRepeat.pScrollBar = scrollP;
				ev.data.sclRepeat.value = value;
				ev.data.sclRepeat.newValue = newValue;
				ev.data.sclRepeat.time = TimGetTicks();

				EvtAddEventToQueue( &ev );

				ev = initEvent( sclExitEvent );
				ev.data.sclExit.scrollBarID = resourceID;
				ev.data.sclExit.pScrollBar = scrollP;
				ev.data.sclExit.value = newValue;
				ev.data.sclExit.newValue = newValue;
				EvtAddEventToQueue( &ev );

				SclSetScrollBar( scrollP, newValue, min, max, pageSize );
		}
	}
}

void showResourceIDs(  actionType type )
{
	FormPtr oldFormP = FrmGetActiveForm();

	Char *selNames[MAX_NO_LISTIDS];
	UInt16 ids[MAX_NO_LISTIDS];

	UInt32 noSelections = 0;

	if ( oldFormP ) {
		if ( type == actionTypeShowFormList ) {
			ids[0] = FrmGetActiveFormID();
			if ( resourceToBuffer( &selNames[0], (char *) FrmGetTitle( oldFormP ) , ids[0] ) ) {
				noSelections++;
			}
		}
 
		else if ( type == actionTypeShowMenuList  ) {
			UInt16 barID = FrmGlueGetMenuBarID ( oldFormP );

			if ( barID != 0 ) {
				MenuBarType *barP = MenuInit ( barID );
				
				if ( barP != NULL ) {
					
					UInt16 pno;
					for ( pno = 0; pno < barP->numMenus; pno++ ) {
						UInt16 itemno;
						MenuPullDownPtr pulldownP =  (MenuPullDownPtr) barP->menus+pno;
						
						if ( pulldownP != NULL ) {
							for ( itemno = 0; itemno < pulldownP->numItems; itemno++ ) {
								MenuItemType *itemP = (MenuItemType *) pulldownP->items+itemno;
								if( *(itemP->itemStr) != MenuSeparatorChar ) {
									if ( resourceToBuffer( &selNames[noSelections], itemP->itemStr, itemP->id ) ) {
										ids[noSelections] = itemP->id;
										noSelections++;
										if ( noSelections == MAX_NO_LISTIDS )
											break;
									}
								}
							}
						}
						if ( noSelections == MAX_NO_LISTIDS )
							break;
					}
					MenuDispose( barP );
				}
			}
		}
		else {
			UInt16 noObjects = FrmGetNumberOfObjects( oldFormP );
			UInt16 objectNo;

			for ( objectNo = 0; objectNo < noObjects; objectNo++ ) {
				FormObjectKind kind = FrmGetObjectType( oldFormP, objectNo );

				UInt8 controlPrefixIndex = 99;

				char controlPrefixes[7][9] = {
					"<ckbox>",
					"<popup>",
					"<select>",
					"<button>",
					"<scroll>",
					"<graph>",
					"<field>"
				};

				char *labelP = NULL;

				switch ( type ) {
				case actionTypeShowControlList:
				{
					switch ( kind ) {
					case frmControlObj:
					{
						ControlPtr controlP = (ControlPtr) FrmGetObjectPtr( oldFormP, objectNo ) ;

						if ( controlP != NULL ) {
							switch ( CtlGlueGetControlStyle( controlP ) ) {
							case checkboxCtl:
								controlPrefixIndex = 0;
								break;
							 case popupTriggerCtl:
								controlPrefixIndex = 1;
								break;
							case selectorTriggerCtl:
								controlPrefixIndex = 2;
								break;
							case buttonCtl:
							case pushButtonCtl:
							case repeatingButtonCtl:
								controlPrefixIndex = 3;
								break;
							default:
								break;
							}
						}							
						break;
					}
					case frmScrollBarObj:
						controlPrefixIndex = 4;
					default:
						break;
					}
					if ( 
						( controlPrefixIndex != 99 ) &&
						( kind == frmControlObj )
					) {
						ControlPtr controlP= (ControlPtr) FrmGetObjectPtr( oldFormP, objectNo ) ;

						if ( controlP != NULL ) {
							if ( CtlGlueIsGraphical( controlP ) ) 
								controlPrefixIndex = 5;
							else
								labelP = (char *) CtlGetLabel( controlP );
						}
					}
					break;
				}
				case actionTypeShowFieldList:
					if ( kind == frmFieldObj ) {
						labelP = FldGetTextPtr( FrmGetObjectPtr( oldFormP, objectNo ) );
						controlPrefixIndex = 6;
					}
					break;
				default:
					break;
				}
				if ( controlPrefixIndex != 99 ) {
					UInt16 resourceID = FrmGetObjectId( oldFormP, objectNo );

					// If we don't have a label for it, use the prefix
					if ( 
						( labelP == NULL ) ||
						( StrLen( labelP ) == 0 )
					)
						labelP = controlPrefixes[controlPrefixIndex];

					if ( resourceToBuffer( &selNames[noSelections], labelP, resourceID ) ) {
						ids[noSelections] = resourceID;
						noSelections++;
						if ( noSelections == MAX_NO_LISTIDS )
							break;
					}
				}
			}
		}
	}

	if ( noSelections == 0 )
		flashBox( "Apt: none found", 1000 );
	else {
		ListType *listP;

		UInt32 selNo;

		Int16 chosen = noSelections;

		FontID oldFont = FntSetFont( stdFont );

		FormPtr newFormP;

		newFormP =  makeFormList( noSelections, &listP);

		if ( newFormP != NULL ) {


			if ( listP != NULL ) {
				chosen = setupList( newFormP, listP, selNames, noSelections );
			}
			
			swapForm( newFormP, oldFormP );
			
			FntSetFont( oldFont );
		}

		fsetFlag( FtrAptAssistResult, ( chosen >= 0 ) && ( chosen < noSelections ),  ids[chosen] );

		for ( selNo = 0; selNo < noSelections; selNo++ ) 
			MemPtrFree( selNames[selNo] );
	}
}

// Better free *buffer when you're finished guy
//
Boolean resourceToBuffer( char **buffer, char *name, UInt16 resID )
{
	if ( resID  > 99999 )
		return false;
	else {
		char numBuff[6];

		IDToString( resID, numBuff );

		if ( 
			( name != NULL ) &&
			( StrLen( name ) > 0 )
		) {
			*buffer = MemPtrNew( sizeof( char ) * ( StrLen( name ) + 2 + StrLen( numBuff ) + 1 ) );

			// Leave space for ': ID'
			StrCopy( *buffer, name );
			StrCat( *buffer, ": " );
		}
		else { 
			*buffer = MemPtrNew( StrLen( numBuff ) + 1 );
			(*buffer)[0] = '\0';
		}

		StrCat( *buffer,  numBuff );
	}

	return true;
}

void simHardButton( actionType action, UInt32 numberOfTimes )
{
	UInt32 i;
	WChar navChars[5] = {  vchrRockerLeft, vchrRockerRight, vchrRockerUp, vchrRockerDown, vchrRockerCenter };
	WChar hardChars[5] = {  vchrHard1, vchrHard2, vchrHard3, vchrHard4, vchrHardPower };
	WChar treoChars[5] = { hsChrSide, vchrHard5, vchrHard6, vchrHard7, vchrHard8 };

	WChar key;

	if ( 
		( action >= actionCatSimHardFirst )  &&
		( action <= actionCatSimHardLast )
	)	
		key = hardChars[action - actionCatSimHardFirst];
	else if ( 
			 ( action >= actionCatSimTreoFirst ) &&
			 ( action <= actionCatSimTreoLast )
   	 )
		key = treoChars[action - actionCatSimTreoFirst];
	else
		key = navChars[action - actionCatSimNavFirst];

	for ( i = 0; i < numberOfTimes; i++ ) {
		//		EvtEnqueueKey ( hsChrModifierKey, 0, commandKeyMask );
		EvtEnqueueKey ( key, 0, commandKeyMask );	
	}
}


void handleRotate( actionType action )
{
	if ( getPINVersion() >=  pinAPIVersion1_1 ) {

		UInt16 newOrient = SysGetOrientation();

		if ( action == actionTypeRotateRight ) {
			if ( newOrient == sysOrientationReverseLandscape )
				newOrient = sysOrientationPortrait;
			else 
				newOrient++;
		}
		else {
			if ( newOrient <= sysOrientationPortrait )
				newOrient = sysOrientationReverseLandscape;
			else 
				newOrient--;
		}
		actionOrientation( newOrient );					
	}
}

void actionOrientation( UInt16 desired )
{
	if (  getPINVersion() >= pinAPIVersion1_1 ) {
		if (  SysGetOrientation() != desired )
			SysSetOrientation( desired );
	}
}

// Thanks to Alex Pruss
//
void actionKhromaTheme( char *themeName )
{
	DmOpenRef ref = DmOpenDatabaseByTypeCreator( KhromaDBType, KhromaDBCreator, dmModeReadOnly );
		
	if ( ref != NULL ) {
		
		UInt16 i;
		UInt16 numRecords = DmNumRecords( ref );
		Boolean stopFlag = false;

		for ( i = 0 ; i < numRecords ; i++ ) {
			MemHandle h = DmQueryRecord( ref, i );
			
			if ( h != NULL && 32 + sizeof( RGBColorType ) <= MemHandleSize( h ) ) {
				void* p = MemHandleLock( h );
				
				if ( ! StrCompare( p, themeName ) ) {
					setColours( ( RGBColorType* )( ( UInt8* )p + 32 ),
							   ( MemHandleSize( h ) - 32 ) / sizeof( RGBColorType ) );
					stopFlag = true;
				}

				MemHandleUnlock( h );
			}

			if ( stopFlag )
				break;
		}
		
		DmCloseDatabase( ref );

		if ( ! stopFlag ) {
			errFlash( "theme not found" );
			//			errFlash( themeName );  could be corrupt
		}
	}
	else
		errFlash( "no KhromaDB" );
}

// Thanks to Alex Pruss
//
void setColours( RGBColorType* c, UInt16 count )
{
	UInt16 i;

	for ( i = 0 ; i < count ; i++ )
		UIColorSetTableEntry( i, c + i );
}

void flashBoxNum( const char *text, Int32 num, Int32 ms )
{
	char numBuff[20];
	char buff[100];

	StrIToA( numBuff, num );	
	StrCopy( buff, text );
	StrCat( buff, ": " );
	StrCat( buff, numBuff );

	flashBox( buff, ms );
}
