#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;
}

