#include <PalmOS.h>
#include "OffFlush_Res.h"

#define Get( f ) ( ( errNone == FtrGet( appCreator, ( f ), &( tempValue ) ) ) ? tempValue : 0 )
#define Set( f, v ) FtrSet( appCreator, ( f ), ( UInt32 )( v ) )

#define STANDALONE
#include "Flush.c"


#define MAX_LEVEL  10

void Register( UInt32 type, UInt16 feature, Boolean state );

typedef struct {
   UInt8 level;  // 0 (never) to 10 (always)
   UInt8 onlyOnButton:1;
   UInt8 fast:1;
} PreferenceType;

PreferenceType pref;


static void EatEvents( void )
{
   EvtFlushKeyQueue();
   EvtFlushPenQueue();
}


void LoadPrefs0( PreferenceType* p )
{
   UInt16 size;

   MemSet( p, sizeof( *p ), 0 );

   p->level = MAX_LEVEL;

   size = 0;

   if ( noPreferenceFound ==
            PrefGetAppPreferences( appCreator, PREF_ID_MAIN, NULL, &size, true ) ) {
       return;
   }

   if ( sizeof( *p ) < size ) {
       size = sizeof( *p );
   }

   PrefGetAppPreferences( appCreator, PREF_ID_MAIN, p, &size, true );
}



void Setup( void )
{
   PreferenceType p0;
   LoadPrefs0( &p0 );

   Register( sysNotifySleepRequestEvent, FTR_sleep, 0 < p0.level );
}


void LoadPrefs( void )
{
   LoadPrefs0( &pref );
}



void SavePrefs0( PreferenceType* p )
{
   PrefSetAppPreferences( appCreator, PREF_ID_MAIN, 0, p,
       sizeof( PreferenceType ), true );
}


void SavePrefs( void )
{
   SavePrefs0( &pref );
}


UInt16 GetObjectIndex( UInt16 id )
{
   return FrmGetObjectIndex( FrmGetActiveForm(), id );
}


void* GetObjectPtr( UInt16 id )
{
   return FrmGetObjectPtr( FrmGetActiveForm(), GetObjectIndex( id ) );
}


Boolean FindMe( UInt16* card, LocalID* id )
{
   DmSearchStateType state;

   return errNone == DmGetNextDatabaseByTypeCreator( true, &state,
              'appl', appCreator,
              false, card, id );
}



void Register( UInt32 type, UInt16 feature, Boolean state )
{

   UInt16  card;
   LocalID id;
   LocalID registeredTo;
   UInt32  tempValue;
   Boolean isRegistered;

   if ( ! FindMe( &card, &id ) )
       return;

   registeredTo = ( LocalID )Get( feature );

   isRegistered = ( registeredTo == id );

   if ( registeredTo != 0 && ! isRegistered )
       Set( feature, 0 );

   if ( ! isRegistered == ! state )
       return;

   if ( state ) {
       SysNotifyRegister( card, id, type, NULL, 0, NULL );
       Set( feature, ( UInt32 )id );
   }
   else {
       SysNotifyUnregister( card, id, type, 0 );
       Set( feature, 0 );
   }
}



static Boolean FreeIsBelow( Int8 level )
{
   UInt32 total;
   UInt32 free;

   if ( level == 0 )
       return false;

   total = CacheSize( &free, NULL );

   return free * MAX_LEVEL < total * level;
}



static void MakeString( Char* s, UInt32 a, UInt32 b )
{
   UInt32 k;

   k = ( a + 512 ) / 1024;

   if ( 1024 <= k ) {
       StrPrintF( s, "%ld.%ldmb", k / 1024, ( ( k * 10 + 512 ) / 1024 ) % 10 );
   }
   else {
       StrPrintF( s, "%ldkb", k );
   }

   if ( b == 0 )
       return;

   b = ( b + 512 ) / 1024;

   StrPrintF( s + StrLen( s ), " (%ld%%)", ( 100 * k + b / 2 ) / b );
}



static void UpdateSizes( void )
{
   UInt32 total, chunk, free;
   static Char totalString[ 24 ];
   static Char chunkString[ 24 ];
   static Char freeString[ 24 ];

   total = CacheSize( &free, &chunk );

   MakeString( totalString, total, 0 );
   MakeString( chunkString, chunk, total );
   MakeString( freeString, free, total );

   FldSetTextPtr( GetObjectPtr( fldTotal ), totalString );
   FldDrawField( GetObjectPtr( fldTotal ) );
   FldSetTextPtr( GetObjectPtr( fldChunk ), chunkString );
   FldDrawField( GetObjectPtr( fldChunk ) );
   FldSetTextPtr( GetObjectPtr( fldFree ), freeString );
   FldDrawField( GetObjectPtr( fldFree ) );
}


static void DrawMain( void )
{
   LstSetSelection( GetObjectPtr( lstLevel ), pref.level );
   CtlSetValue( GetObjectPtr( chkOnlyOnButton ), pref.onlyOnButton );
   CtlSetValue( GetObjectPtr( chkFast ), pref.fast );

   FrmDrawForm( FrmGetActiveForm() );

   UpdateSizes();
}



static Boolean Handler_frmMain( EventType* event )
{
   Boolean handled;
   handled = false;

   switch (event->eType) {
       case frmOpenEvent:
           DrawMain();
           handled = true;
           break;

       case ctlSelectEvent: {
           switch ( event->data.ctlSelect.controlID ) {
               case chkOnlyOnButton: {
                   pref.onlyOnButton = !! CtlGetValue( GetObjectPtr( chkOnlyOnButton ) );
                   SavePrefs();
                   handled = true;
                   break;
               }
               case chkFast: {
                   pref.fast = !! CtlGetValue( GetObjectPtr( chkFast ) );
                   SavePrefs();
                   handled = true;
                   break;
               }
               case btnFlushNow: {
                   DBCacheFlush( !! CtlGetValue( GetObjectPtr( chkFast ) ) );
                   EatEvents();
                   DrawMain();
                   handled = true;
                   break;
               }
           }
       }

       case lstSelectEvent: {
           Int16 item;

           item = event->data.lstSelect.selection;

           switch ( event->data.lstSelect.listID ) {
               case lstLevel:
                   if ( 0 <= item ) {
                       pref.level = item;
                       SavePrefs();
                       Setup();
                   }
                   handled = true;
                   break;
           }
           break;
       }

       default:
           break;
   }
   return handled;
}



static void EventLoop( void )
{
   EventType event;
   Boolean   handled;

   do {
       EvtGetEvent( &event, evtWaitForever );

       if ( FrmGetActiveFormID() == frmMain && event.eType == keyDownEvent )
           UpdateSizes();

       handled = SysHandleEvent( &event );

       if ( ! handled ) {

           switch ( event.eType ) {
               case frmLoadEvent: {
                   UInt16    formID;
                   FormType* form;

                   formID = event.data.frmLoad.formID;
                   form = FrmInitForm( formID );

                   FrmSetActiveForm( form );

                   switch ( formID ) {
                       case frmMain:
                           FrmSetEventHandler( form, Handler_frmMain );
                           break;
                   }
                   handled = true;
               }
               default:
                   handled = FrmDispatchEvent( &event );
           }
       }
   } while ( event.eType != appStopEvent );

   FrmCloseAllForms();
}



void HandleNotification( SysNotifyParamType* np )
{
   switch ( np->notifyType ) {
//        case sysNotifyVirtualCharHandlingEvent:
//            if ( ( ( SysNotifyVirtualCharHandlingType* )( np->notifyDetailsP ) )->keyDown.chr == vchrHardPower )
//                DBCacheFlush();
       case sysNotifySleepRequestEvent: {
           PreferenceType p0;

           LoadPrefs0( &p0 );

           if ( ( ! p0.onlyOnButton ||
                  ( ( SleepEventParamType* ) ( np->notifyDetailsP ) )->reason == sysSleepPowerButton ) &&
                FreeIsBelow( p0.level ) ) {
               DBCacheFlush( p0.fast );
               EatEvents();
           }
           break;
       }
   }
}



void Start( void )
{
   LoadPrefs();
   FrmGotoForm( frmMain );
}


UInt32 PilotMain( UInt16 command, void* cmdPBP, UInt16 flags )
{
   switch ( command ) {
       case sysAppLaunchCmdNotify: {
           HandleNotification( (SysNotifyParamType *)cmdPBP );
           break;
       }
       case sysAppLaunchCmdSystemReset:
       case sysAppLaunchCmdSyncNotify:
           Setup();
           break;
       case sysAppLaunchCmdNormalLaunch:
           Setup();
           Start();
           EventLoop();
           break;
   }
   return 0;
}