#define USE_FTR

#include <PalmOS.h>
#include <VFSMgr.h>
#include <68k/system/PmPalmOSNVFS.h>
#ifndef STANDALONE
#include "Backup.h"
#endif

#ifndef memHeapFlagReadOnly
# define memHeapFlagReadOnly 0x0001
#endif

// To use DBCacheFlush() code in another application modify these defines:
#define CREATOR                 appCreator
#define FIRST_FTR_ID    1

void DBCacheFlush( Boolean fast );

#ifdef DA
void DA_Main( void )
{
   DBCacheFlush( true );
}
#endif



static void SetRecyclable( UInt16 card, LocalID id )
{
   UInt16 attr;

   if ( id == NULL )
       return;

   if ( errNone == DmDatabaseInfo( card, id, NULL, &attr, NULL,
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ) ) {

       attr = attr | dmHdrAttrRecyclable;
       DmSetDatabaseInfo( card, id, NULL, &attr, NULL,
                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
   }

}


static Boolean HaveNVFS( void )
{
   Err err;
   UInt32 value;

   err = FtrGet( sysFtrCreator, sysFtrNumDmAutoBackup, &value );

   return ( err == errNone && value != 0 );
}



static void Progress( Boolean show )
{
#if 0
    Coord w;
    Coord h;

    if ( ! show )
        return;

    WinGetDisplayExtent( &w, &h );

    WinDrawPixel( SysRandom(0)%w, SysRandom(0)%h );
#endif
}



// Flushing via feature memory (Does not work well for lower NVFS version [bellow TX])
// Better in defragmentation
static void FtrFlush( UInt16 storageHeapId, Boolean savedScreen )
{
       #define BLOCK_SIZE  65536       // 32768
       Int32     needed ;
       UInt16    fNum ;
       Int32     i ;
       void     *f ;
       UInt32    size ;
       UInt32    dbCacheTotal = MemHeapSize( storageHeapId|dbCacheFlag );

       // First algorithm
       fNum = FIRST_FTR_ID;
       for( i=70 ; 10 <= i ; i-=10 )
       {
               Progress( savedScreen );
               size = dbCacheTotal*i/100 ;
               if( FtrPtrNew( CREATOR, fNum, size, &f) == 0 )
                       fNum++ ;
       }

       needed = dbCacheTotal / BLOCK_SIZE;
       for( i=0 ; i < needed ; i++ )
       {
               Progress( savedScreen );
               if( FtrPtrNew( CREATOR, fNum, BLOCK_SIZE, &f) != 0 )
                       break ;
               fNum++ ;
       }

       for( i=FIRST_FTR_ID ; i < fNum ; i++ )
               FtrPtrFree( CREATOR, i );

       // Second algorithm
       for( fNum=FIRST_FTR_ID ; ; fNum++ )
       {
               UInt32 free ;

               Progress( savedScreen );

               MemHeapFreeBytes( storageHeapId|dbCacheFlag, &free, &size );

               if( size < 500 )
                       break ;

               if( FtrPtrNew( CREATOR, fNum, size, &f) != 0 )
                       break ;

               do {
                       Progress( savedScreen );
                       size += BLOCK_SIZE ;
               }
               while( MemPtrResize(f, size) == 0 ) ;
       }

       for( i=FIRST_FTR_ID ; i < fNum ; i++ )
               FtrPtrFree( CREATOR, i );
}


void GetFlushName( Char* s )
{
   *s++ = 'F';
   *s++ = 'l';
   *s++ = 'u';
   *s++ = 's';
   *s++ = '-';
   *s++ = 't';
   *s++ = 'm';
   *s++ = 'p';
   *s++ = '.';
   *s++ = 'f';
   *s++ = 'l';
   *s++ = 'u';
   *s++ = 's';
   *s++ = 'h';
   *s = 0;
}


// Flushing via DB writes.
// Procedure suitable for all NVFS versions
static void DBFlush( UInt16 storageHeapId, Boolean savedScreen )
{
       #define BLK_SIZE        30000

       UInt32          dbCacheTotal ;
       DmOpenRef       ref = NULL ;

       // Get available DB space.
       // Must be both bellow the DbCache capacity and bellow the free storage space.
       {
               #define MIN_FREE_STORAGE        100000L

               UInt32 free, maxBytes;
               MemHeapFreeBytes( storageHeapId, &free, &maxBytes ) ;
               if( free < MIN_FREE_STORAGE )
                       return ;

               dbCacheTotal = MemHeapSize( storageHeapId|dbCacheFlag );
               if( dbCacheTotal > free )
                       dbCacheTotal = free ;

               dbCacheTotal -= MIN_FREE_STORAGE ;
       }

       // Open tmp recyclable database
       {
               char flushName[ dmDBNameLength ];
               LocalID    dbId;

               GetFlushName( flushName );
               dbId = DmFindDatabase( 0, flushName );
               if( dbId != NULL )
                       DmDeleteDatabase( 0, dbId );

               DmCreateDatabase( 0, flushName, CREATOR, 'temp', false );

               dbId = DmFindDatabase( 0, flushName );
               if( dbId != NULL )
               {
                       //SetRecyclable
                       SetRecyclable( 0, dbId );
                       ref = DmOpenDatabase( 0, dbId, dmModeReadWrite ) ;
                       if( ref == NULL )
                               DmDeleteDatabase( 0, dbId );
               }
       }

       if( ref != NULL )
       {
               // Fill DB records
               Int32 nNeededRecs = dbCacheTotal / BLK_SIZE ;

               while( nNeededRecs-- > 0 )
               {
                       UInt16 location = dmMaxRecordIndex;

                       Progress( savedScreen );

                       if( DmNewRecord( ref, &location, BLK_SIZE) == NULL )
                               break;
               }

               // Cleanup
               {
                       // This would save quite a few seconds (prevents unneeded commit to NVFS),
                       // but does not work on T650.
                       // UInt16 n_recs = DmNumRecords( ref ) ;
                       // while( n_recs-- != 0 )
                       //   DmDeleteRecord( ref, n_recs ) ;

                       DmCloseDatabase( ref ) ;        // This deletes the DB
               }
       }
}


UInt32 CacheSize( UInt32* freeP, UInt32* chunkP )
{
               UInt16 numHeaps = MemNumHeaps(0);
               UInt16 i;
               for( i=0 ; i < numHeaps; i++ )
               {
                       UInt16 heapId = MemHeapID(0, i);

                       if( !MemHeapDynamic(heapId) )
                       {
                               UInt16 heapFlags = MemHeapFlags( heapId ) ;

                               if( (heapFlags & 0x1) == 0 )  // memHeapFlagReadOnly==0x01
                               {
                                    UInt32 free;
                                    UInt32 chunk;

                                    MemHeapFreeBytes( heapId|dbCacheFlag, &free, &chunk );
                                    if ( freeP != NULL )
                                        *freeP = free;
                                    if ( chunkP != NULL )
                                        *chunkP = chunk;
                                    return MemHeapSize( heapId|dbCacheFlag );
                               }
                       }
                }
                return 0;
}



void GetMessage( Char* m )
{
   *m++ = 'F';
   *m++ = 'l';
   *m++ = 'u';
   *m++ = 's';
   *m++ = 'h';
   *m++ = 'i';
   *m++ = 'n';
   *m++ = 'g';
   *m++ = '.';
   *m++ = '.';
   *m++ = '.';
   *m = 0;
}


void DBCacheFlush( Boolean fast )
{
       // Attention!!!
       //
       // No system processing is allowed during DbCache flush. (That's way user abort is not implemented.)
       // Otherwise the OS may negatively influence the results. (Proved on T650.)

               UInt16 numHeaps = MemNumHeaps(0);
               UInt16 i;
               WinHandle oldWinH;
               void* bits;
               RectangleType r;
               Err           err;
               Boolean       savedScreen;

               if ( ! HaveNVFS() )
                   return;

               oldWinH = WinSetDrawWindow( WinGetDisplayWindow() );

               WinGetDrawWindowBounds( &r );

               bits = WinSaveBits( &r, &err );

               if ( err != errNone ) {
                   savedScreen = false;
               }
               else {
                   RectangleType rect;
                   Char          message[ 60 ];
                   Coord         messageSize;

                   savedScreen = true;

                   GetMessage( message );

                   messageSize = FntCharsWidth( message, StrLen( message ) );

                   rect.topLeft.x = 80 - messageSize / 2 - 14;
                   rect.extent.x  = messageSize + 28;
                   rect.topLeft.y = 70-4;
                   rect.extent.y  = 28;

                   WinEraseRectangle( &rect, 0 );
                   WinDrawRectangleFrame( dialogFrame, &rect );

                   WinDrawChars( message, StrLen( message ), 80 - messageSize / 2, 80 - 5 );

               }

               // Get storage heap characteristics
               for( i=0 ; i < numHeaps; i++ )
               {
                       UInt16 heapId = MemHeapID(0, i);

                       if( !MemHeapDynamic(heapId) )
                       {
                               UInt16 heapFlags = MemHeapFlags( heapId ) ;

                               if( (heapFlags & 0x1) == 0 )  // memHeapFlagReadOnly==0x01
                               {
                                       DmSync() ;
                                       if ( ! fast )
                                           DBFlush( heapId, savedScreen ) ;
                                       FtrFlush( heapId, savedScreen ) ;
                                       DmSync() ;
                                       break ;
                               }
                       }
               }

               if ( savedScreen ) {
                   WinRestoreBits( bits, 0, 0 );
                   MemPtrFree( bits );
               }
               WinSetDrawWindow( oldWinH );
}