/* ================================================================================== */
/* Copyright (c) 1998-1999 3Com Corporation or its subsidiaries. All rights reserved. */
/* ================================================================================== */

#include "EmulatorCommon.h"
#include "CPU_REG.h"

#include <sys/stat.h>
#include <unistd.h>

#include "MapFile.h"
#include "Preferences.h"

// -----------------------------------------------------------------------------
//	Globals and constants
// -----------------------------------------------------------------------------

Preferences gPreferences;
Preferences *gHostPrefs = &gPreferences; // also gPrefs
const char USERPREFS[] = ".poserrc";

// -----------------------------------------------------------------------------
// constructor(s) / destructor
// -----------------------------------------------------------------------------

Preferences::Preferences( void )
{
    // defaults

	_serialDevice = "/dev/ttyS0";
	_skinFilePath = BASE_SKIN_NAME;
	fSkinFiles.push_back( string( BASE_SKIN_NAME ) );
    
	fLastConfiguration.fDeviceType = kDevicePalmIII;
	fLastConfiguration.fSilkscreenType = kSilkscreenEnglish;
	fLastConfiguration.fRAMSize = 2048;
	fScale = 1;
}

Preferences::~Preferences( void )
{
}

// -----------------------------------------------------------------------------
// public methods
// -----------------------------------------------------------------------------

// Helper macros for getting & setting prefs
#define LOADSTRING( key, val ) {char *_v = map.find( key ); if (_v!=NULL) val=_v;}
#define LOADFILEREF( key, val ) {char *_v = map.find( key ); if (_v!=NULL) val=FileReference(_v);}
#define LOADBOOL( key, val ) { char *_v = map.find( key );                                                \
    if ((_v!=NULL)&&((strcasecmp(_v,"true")==0)||(strcasecmp(_v,"yes")==0)||(strcasecmp(_v,"on")==0)))   \
        val=true;  else val=false; }
#define LOADINT( key, val ) { char *_v = map.find( key ); if(_v!=NULL) { int _i; sscanf(_v,"%d",&_i);(val)=_i;}}

#define SAVESTRING( key, val ) fprintf(f,"%s = %s\n",key,val.c_str())
#define SAVEFILEREF( key, val ) fprintf(f,"%s = %s\n",key,val.GetFilePath())
#define SAVEBOOL( key, val ) fprintf(f,"%s = %s\n",key,(val)?"true":"false" )
#define SAVEINT( key, val, ts ) fprintf(f,"%s = "ts"\n",key,val)

// -----------------------------------------------------------------------------
// load user prefs
bool Preferences::loadFromFile()
{
    char fname[PATH_MAX];
    sprintf( fname, "%s/%s", getenv( "HOME" ), USERPREFS );
    
    MapFile map( fname );

    LOADSTRING( "SerialPort", _serialDevice );
    LOADSTRING( "SkinFile", _skinFilePath );

    // load the list of other skin files.
    int n=0;
    while ( true )
    {
        char key[PATH_MAX];
        sprintf( key, "SkinFileList%d", n++ );
        char *fname = map.find( key );
        if ( fname != NULL )
            fSkinFiles.push_back( string( fname ) );
        else
        {
            break;
        }
    }

	int	dummy1 = (int) fLastConfiguration.fDeviceType;
	int dummy2 = (int) fLastConfiguration.fSilkscreenType;
    LOADINT( "DeviceType", dummy1 );
    LOADINT( "SilkscreenType", dummy2 );
    LOADINT( "RAMSize", fLastConfiguration.fRAMSize );
    LOADFILEREF( "ROMFile", fLastConfiguration.fROMFile );

	fLastConfiguration.fDeviceType = (DeviceType) dummy1;
	fLastConfiguration.fSilkscreenType = (SilkscreenType) dummy2;

    LOADINT( "GremlinsNumber", fGremlinInfo.fNumber );
    LOADINT( "GremlinsSteps", fGremlinInfo.fSteps );
	LOADINT( "GremlinsSaveFrequency", fGremlinInfo.fSaveFrequency );
    LOADBOOL( "GremlinsContinue", fGremlinInfo.fContinuePastWarnings );

    LOADINT( "ScreenScale", fScale );
    LOADINT( "ScreenBackground", fScreenBackground );
    LOADINT( "WindowFrame", fWindowFrame );
    LOADINT( "CloseAction", fCloseAction );

    LOADBOOL( "RedirectNetLib", fRedirectNetLib );
    LOADBOOL( "ReportCorruptedHeap", fReportCorruptedHeap );
    LOADBOOL( "ReportFreeChunkAccess", fReportFreeChunkAccess );
    LOADBOOL( "ReportHardwareRegisterAccess", fReportHardwareRegisterAccess );
    LOADBOOL( "ReportInvalidPC", fReportInvalidPC );
    LOADBOOL( "ReportLowMemoryAccess", fReportLowMemoryAccess );
    LOADBOOL( "ReportLowStackAccess", fReportLowStackAccess );
    LOADBOOL( "ReportMemMgrDataAccess", fReportMemMgrDataAccess );
    LOADBOOL( "ReportMemMgrSemaphore", fReportMemMgrSemaphore );
    LOADBOOL( "ReportScreenAccess", fReportScreenAccess );
    LOADBOOL( "ReportStackAlmostOverflow", fReportStackAlmostOverflow );
    LOADBOOL( "ReportStackOverflow", fReportStackOverflow );
    LOADBOOL( "ReportStorageHeapAccess", fReportStorageHeapAccess );
    LOADBOOL( "ReportSysFatalAlert", fReportSysFatalAlert );
    LOADBOOL( "ReportSystemGlobalAccess", fReportSystemGlobalAccess );
    LOADBOOL( "ReportUnhandledException", fReportUnhandledException );
    LOADBOOL( "ReportUnimplementedTrap", fReportUnimplementedTrap );
    LOADBOOL( "ReportUninitializedChunkAccess", fReportUninitializedChunkAccess );
    LOADBOOL( "ReportUninitializedStackAccess", fReportUninitializedStackAccess );
    LOADBOOL( "ReportUnlockedChunkAccess", fReportUnlockedChunkAccess );

    LOADINT( "LogErrorMessages", fLogErrorMessages );
    LOADINT( "LogWarningMessages", fLogWarningMessages );
    LOADINT( "LogGremlins", fLogGremlins );
    LOADINT( "LogCPUOpcodes", fLogCPUOpcodes );
    LOADINT( "LogEnqueuedEvents", fLogEnqueuedEvents );
    LOADINT( "LogDequeuedEvents", fLogDequeuedEvents );
    LOADINT( "LogSystemCalls", fLogSystemCalls );
    LOADINT( "LogApplicationCalls", fLogApplicationCalls );
    LOADINT( "LogSerial", fLogSerial );
    LOADINT( "LogSerialData", fLogSerialData );
    LOADINT( "LogNetLib", fLogNetLib );
    LOADINT( "LogNetLibData", fLogNetLibData );
    LOADINT( "LogExgMgr", fLogExgMgr );
    LOADINT( "LogExgMgrData", fLogExgMgrData );
    LOADINT( "LogHLDebugger", fLogHLDebugger );
    LOADINT( "LogHLDebuggerData", fLogHLDebuggerData );
    LOADINT( "LogLLDebugger", fLogLLDebugger );
    LOADINT( "LogLLDebuggerData", fLogLLDebuggerData );

    LOADBOOL( "ReportMemoryLeaks", fReportMemoryLeaks );
    LOADBOOL( "ReportLockedRecords", fReportLockedRecords );

    LOADBOOL( "FillNewBlocks", fFillNewBlocks );
    LOADBOOL( "FillResizedBlocks", fFillResizedBlocks );
    LOADBOOL( "FillDisposedBlocks", fFillDisposedBlocks );
    LOADBOOL( "FillStack", fFillStack );
    
    LOADSTRING( "UserName", fUserName );

    return true;
}

// -----------------------------------------------------------------------------
// store this pref object to the given file. Create if not present. Overwrite if
// the file is already present. Return true if saved OK.
bool Preferences::storeToFile()
{
    char fname[PATH_MAX];
    sprintf( fname, "%s/%s", getenv( "HOME" ), USERPREFS );
    FILE *f = fopen( fname, "w" );

    if ( f != NULL )
    {
        fprintf( f, "# POSER Preferences\n\n");

        SAVESTRING( "SerialPort", _serialDevice );
        SAVESTRING( "SkinFile", _skinFilePath );

        // check the skins list
        statSkinFileList();

        // then save it
        ParamList::iterator skinIter;
        int n=0;
        for (skinIter=fSkinFiles.begin(); skinIter != fSkinFiles.end(); n++ )
        {
            string skinFile = *skinIter++;
            if ( strcmp( skinFile.c_str(), BASE_SKIN_NAME ) != 0 )
            {
                char key[PATH_MAX];
                sprintf( key, "SkinFileList%d", n );
                SAVESTRING( key, skinFile );
            }
            else n--;
        }

        SAVEINT( "DeviceType", (long) fLastConfiguration.fDeviceType, "%ld" );
        SAVEINT( "SilkscreenType", (long) fLastConfiguration.fSilkscreenType, "%ld" );
        SAVEINT( "RAMSize", fLastConfiguration.fRAMSize, "%ld" );
        SAVEFILEREF( "ROMFile", fLastConfiguration.fROMFile );

        SAVEINT( "GremlinsNumber", fGremlinInfo.fNumber, "%ld" );
        SAVEINT( "GremlinsSteps", fGremlinInfo.fSteps, "%ld" );
		SAVEINT( "GremlinsSaveFrequency", fGremlinInfo.fSaveFrequency, "%ld" );
        SAVEBOOL( "GremlinsContinue", fGremlinInfo.fContinuePastWarnings );

        SAVEINT( "ScreenScale", fScale, "%d" );
        SAVEINT( "ScreenBackground", fScreenBackground, "%d" );
        SAVEINT( "WindowFrame", fWindowFrame, "%d" );
        SAVEINT( "CloseAction", fCloseAction, "%d" );

        SAVEBOOL( "RedirectNetLib", fRedirectNetLib );
        SAVEBOOL( "ReportCorruptedHeap", fReportCorruptedHeap );
        SAVEBOOL( "ReportFreeChunkAccess", fReportFreeChunkAccess );
        SAVEBOOL( "ReportHardwareRegisterAccess", fReportHardwareRegisterAccess );
        SAVEBOOL( "ReportInvalidPC", fReportInvalidPC );
        SAVEBOOL( "ReportLowMemoryAccess", fReportLowMemoryAccess );
        SAVEBOOL( "ReportLowStackAccess", fReportLowStackAccess );
        SAVEBOOL( "ReportMemMgrDataAccess", fReportMemMgrDataAccess );
        SAVEBOOL( "ReportMemMgrSemaphore", fReportMemMgrSemaphore );
        SAVEBOOL( "ReportScreenAccess", fReportScreenAccess );
        SAVEBOOL( "ReportStackAlmostOverflow", fReportStackAlmostOverflow );
        SAVEBOOL( "ReportStackOverflow", fReportStackOverflow );
        SAVEBOOL( "ReportStorageHeapAccess", fReportStorageHeapAccess );
        SAVEBOOL( "ReportSysFatalAlert", fReportSysFatalAlert );
        SAVEBOOL( "ReportSystemGlobalAccess", fReportSystemGlobalAccess );
        SAVEBOOL( "ReportUnhandledException", fReportUnhandledException );
        SAVEBOOL( "ReportUnimplementedTrap", fReportUnimplementedTrap );
        SAVEBOOL( "ReportUninitializedChunkAccess", fReportUninitializedChunkAccess );
        SAVEBOOL( "ReportUninitializedStackAccess", fReportUninitializedStackAccess );
        SAVEBOOL( "ReportUnlockedChunkAccess", fReportUnlockedChunkAccess );

        SAVEINT( "LogErrorMessages", fLogErrorMessages, "%d" );
        SAVEINT( "LogWarningMessages", fLogWarningMessages, "%d" );
        SAVEINT( "LogGremlins", fLogGremlins, "%d" );
        SAVEINT( "LogCPUOpcodes", fLogCPUOpcodes, "%d" );
        SAVEINT( "LogEnqueuedEvents", fLogEnqueuedEvents, "%d" );
        SAVEINT( "LogDequeuedEvents", fLogDequeuedEvents, "%d" );
        SAVEINT( "LogSystemCalls", fLogSystemCalls, "%d" );
        SAVEINT( "LogApplicationCalls", fLogApplicationCalls, "%d" );
        SAVEINT( "LogSerial", fLogSerial, "%d" );
        SAVEINT( "LogSerialData", fLogSerialData, "%d" );
        SAVEINT( "LogNetLib", fLogNetLib, "%d" );
        SAVEINT( "LogNetLibData", fLogNetLibData, "%d" );
        SAVEINT( "LogExgMgr", fLogExgMgr, "%d" );
        SAVEINT( "LogExgMgrData", fLogExgMgrData, "%d" );
        SAVEINT( "LogHLDebugger", fLogHLDebugger, "%d" );
        SAVEINT( "LogHLDebuggerData", fLogHLDebuggerData, "%d" );
        SAVEINT( "LogLLDebugger", fLogLLDebugger, "%d" );
        SAVEINT( "LogLLDebuggerData", fLogLLDebuggerData, "%d" );

        SAVEBOOL( "ReportMemoryLeaks", fReportMemoryLeaks );
        SAVEBOOL( "ReportLockedRecords", fReportLockedRecords );

        SAVEBOOL( "FillNewBlocks", fFillNewBlocks );
        SAVEBOOL( "FillResizedBlocks", fFillResizedBlocks );
        SAVEBOOL( "FillDisposedBlocks", fFillDisposedBlocks );
        SAVEBOOL( "FillStack", fFillStack );
    
        SAVESTRING( "UserName", fUserName );

        fclose( f );
    }
        
    return true;
}

//-----------------------------------------------------------------------------
// stat each item in the fSkinFiles list, and remove unknown files
void Preferences::statSkinFileList()
{
    ParamList::iterator skinIter;
    for (skinIter=fSkinFiles.begin(); skinIter != fSkinFiles.end(); )
    {
        struct stat statbuf;
        string skinFile = *skinIter++;

        if ( ( strcmp( skinFile.c_str(), BASE_SKIN_NAME ) != 0 ) &&
             ( stat( skinFile.c_str(), &statbuf ) != 0 ) )
        {
            fSkinFiles.remove( skinFile );
        }
    }
}
