// 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 );
}