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

#include "EmulatorCommon.h"

#include "CPU_REG.h"	// Emulator::, others
#include "RAM_ROM.h"	// Memory::
#include "Gremlins.h"	// Gremlins_New...

#include <limits.h>

#define down fl_down	// "down" defined both in WindowNew.h and Fl/Enumerations.h
#include <Fl.H>

#include "fltk_Dialog.h"	// showNewGremlinWindow

#include "CPU_MT.h"
#include "Document.h"
#include "Preferences.h"

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

Document::Document( const AppPreferences::Configuration& cfg )
        : _currentConfiguration( cfg )
{
    _cpu = new CPU;

	// Try initializing the emulator with all this information.
	ErrCode	error = _cpu->initialize( _currentConfiguration );

	if (error != errNone)
    {
        _cpu->dispose();
        _cpu = NULL;
    }
}

Document::~Document( void )
{
    propertyChanged( documentClosing );

    if ( _cpu != NULL )
    {
        Platform::GCW_Close();
        _cpu->dispose();
        _cpu = NULL;
    }
}

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

// -----------------------------------------------------------------------------
// read the document state from the given file, then get the CPU going with it.
ErrCode Document::open( FileReference& ramFileRef )
{
    ErrCode	result = _cpu->load( ramFileRef );

	if (result == errNone)
	{
		// If we loaded a session file with a Gremlin running, turn
		// it off, but let the user turn it back on.

		if (Gremlins_IsOn ())
		{
			Gremlins_Stop ();
			Platform::GCW_Open ();
		}

		_currentRAMRef = ramFileRef;
        _cpu->run();

        propertyChanged( documentOpening );
	}

	return result;
}

// -----------------------------------------------------------------------------
// start the emulator running
void Document::emulatorStart( void )
{
    _cpu->run();
    propertyChanged( cpuStatusChanged );
}

// -----------------------------------------------------------------------------
// stop the emulator
void Document::emulatorStop( void )
{
    _cpu->stop();
    propertyChanged( cpuStatusChanged );
}

// -----------------------------------------------------------------------------
// reset the emulator
void Document::emulatorReset( void )
{
    _cpu->reset();
    propertyChanged( cpuStatusChanged );
}

// -----------------------------------------------------------------------------
// hotsync helper function
void _hotSyncUp( void *data )
{
    Document *doc = (Document*) data;
	doc->_cpu->hotsync( false );
}

// -----------------------------------------------------------------------------
// Start hotsync in the emulator
void Document::hotsync( void )
{
    // push hotsync button down
	_cpu->hotsync( true );

    // install a timer to have the hotsync button released in 1 second:
    Fl::add_timeout( 1.0, _hotSyncUp, this );
}

// -----------------------------------------------------------------------------
// load the given application file
void Document::loadApplication( FileReference& appFile )
{
    // stop the CPU and load the app
    CPUStopper stopper( _cpu, "load an application" );
    if (stopper.Stopped())
    {
        LoadableList fileList;
        fileList.push_back( appFile );
        
        ::LoadPalmFileList( fileList );
    }
}

// -----------------------------------------------------------------------------
// save the memory image to current file
void Document::saveMemory()
{
	CPUStopper stopper( _cpu, "Save RAM image" );

	if ( stopper.Stopped() )
	{
		saveRAMImage( _currentRAMRef, false );
        propertyChanged();
	}
}

// -----------------------------------------------------------------------------
// save the memory image to current file
void Document::saveMemoryAs( FileReference& dest )
{
	CPUStopper stopper( _cpu, "Save RAM image" );

	if ( stopper.Stopped() )
	{
		saveRAMImage( dest, false );
        propertyChanged();
	}
}

// -----------------------------------------------------------------------------
// save the memory image to the given file ref
void Document::saveScreen( FileReference& dest )
{
	CPUStopper stopper( _cpu, "Save LCD ScreenShot");
    
	if ( stopper.Stopped() )
	{
        //! TODO
        //_cpu->saveScreenShot( dest );
	}
}

// -----------------------------------------------------------------------------
// add the given callback to the list of PropertyChangedHandlers
void Document::addPropertyChangedHandler( PropertyChangedHandler callback, void *param )
{
    PropertyChangedHandlerSpec *spec = new PropertyChangedHandlerSpec;
    spec->func = callback;
    spec->param = param;
    
    _handlers.push_back( spec );
}

// -----------------------------------------------------------------------------
// remove the given callback from the list of PropertyChangedHandlers
void Document::removePropertyChangedHandler( PropertyChangedHandler callback )
{
	list<PropertyChangedHandlerSpec*>::iterator funcIter;

	for (funcIter = _handlers.begin (); funcIter != _handlers.end (); )
	{
        PropertyChangedHandlerSpec *spec = *funcIter++;
        if ( spec->func == callback )
        {
            _handlers.remove( spec );
            delete spec;
            return;
        }
	}
}

// -----------------------------------------------------------------------------
// Return true if the HW LCD is on
bool Document::LcdIsOn()
{
    return HWRegisters::LCDIsOn();
}

// -----------------------------------------------------------------------------
// gremlins control functions
void Document::gremlinsNew()
{
	CPUStopper stopper( _cpu, "Gremlins New");
    
	if ( stopper.Stopped() )
	{
		if ( showNewGremlinWindow() )
		{
			Gremlins_New();
		}
	}
}

void Document::gremlinsStep()
{
	CPUStopper stopper( _cpu, "Gremlins Step");
    
	if ( stopper.Stopped() )
	{
        Gremlins_Step();
	}
}

void Document::gremlinsResume()
{
	CPUStopper stopper( _cpu, "Gremlins Resume");
    
	if ( stopper.Stopped() )
	{
        Gremlins_Resume();
	}
}

void Document::gremlinsStop()
{
	CPUStopper stopper( _cpu, "Gremlins Stop");
    
	if ( stopper.Stopped() )
	{
        Gremlins_Stop();
	}
}

// -----------------------------------------------------------------------------
// private methods
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// save the ram image to the file
void Document::saveRAMImage( const FileReference& ramFileRef, Bool forAutoSave )
{
	// See if there's a given name we should use.
	if ( !ramFileRef.IsSpecified() )
	{
        return;
    }

	try
	{
		Emulator::Save( ramFileRef, !forAutoSave, !forAutoSave, !forAutoSave );
	}
	catch (...)
	{
	}

    _currentRAMRef = ramFileRef;
}

// -----------------------------------------------------------------------------
// broadcast changed notices to all propertyChangedHandlers
void Document::propertyChanged( int property )
{
	list<PropertyChangedHandlerSpec*>::iterator funcIter;

	for (funcIter = _handlers.begin (); funcIter != _handlers.end (); )
	{
        PropertyChangedHandlerSpec *spec = *funcIter++;
        (*spec->func)( property, spec->param );
	}
}
