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

