/*
 * Engine.java
 *
 * Created on 21 Mayıs 2005 Cumartesi, 22:43
 */

package com.isoft.mmcd.engine ;

import com.isoft.mmcd.utils.* ;
import com.isoft.mmcd.application.* ;
import com.isoft.mmcd.application.handlers.* ;
import com.isoft.mmcd.application.rnk.RNKApplication ;
import com.isoft.mmcd.application.rnk.RnkFirstApplication;
import com.isoft.mmcd.application.rnk.RnkTranslatorApplication;
import com.isoft.mmcd.application.events.* ;
import com.isoft.mmcd.skin.* ;
import com.isoft.mmcd.skin.rnk.RnkFirstSkin;
import com.isoft.mmcd.skin.rnk.RnkSkin ;
import com.isoft.mmcd.exceptions.* ;
import com.isoft.mmcd.utils.cfgreader.* ;
import com.isoft.mmcd.engine.listeners.* ;

import java.awt.* ;
import javax.swing.* ;

/**
 *
 * @author Fatih
 */
public class Engine implements Runnable, java.io.Serializable
{
    static
    {
        boolean assertsEnabled = false ;
        assert ( assertsEnabled = true ) == true ; // Intentional side effect!!!
        if( !assertsEnabled )
            throw new RuntimeException( "Asserts must be enabled!!! Add -ea parameter to JVM" ) ;
    }

    protected EventPool eventPool = new EventPool( ) ;

    protected MCCompoundProperty configuration = new MCCompoundProperty( ) ;

    protected MMCDApplication application = null ;

    protected MMCDSkin skin = null ;

    protected MMCDEventQueue eventQueue = new MMCDEventQueue( ) ;

    protected MMCDListenerPool listenerPool = new MMCDListenerPool( ) ;

    protected EventHandlerPool eventHandlerPool = new EventHandlerPool( ) ;

    protected boolean isfullscreen = false ;

    protected boolean isrunning = true ;

    protected MCCompoundProperty autoexecProps = null ;

    protected MCCompoundProperty autoFinalizeProps = null ;

    protected DisplayMode origdisplaymode ;

    /** Creates a new instance of Engine */
    public Engine( )
    {
        /* nothing to do */
    }

    public MMCDApplication getApplication( )
    {
        return application ;
    }

    public RNKApplication getRNKApplication( )
    {
        if( this.application instanceof RNKApplication )
            return ( RNKApplication )application ;
        return null ;
    }

    public RnkFirstApplication getRnkFirstApplication( )
    {
        if( this.application instanceof RnkFirstApplication )
            return ( RnkFirstApplication )application ;
        return null ;
    }

    public RnkTranslatorApplication getRnkTranslatorApplication( )
    {
        if( this.application instanceof RnkTranslatorApplication )
            return ( RnkTranslatorApplication )application ;
        return null ;
    }

    public void setApplication( MMCDApplication application )
    {
        this.application = application ;
    }

    public MMCDSkin getSkin( )
    {
        return skin ;
    }

    public void setSkin( MMCDSkin skin )
    {
        this.skin = skin ;
    }

    public RnkSkin getRnkSkin( )
    {
        if( this.getSkin( ) instanceof RnkSkin )
            return ( RnkSkin )this.getSkin( ) ;

        return null ;
    }

    public RnkFirstSkin getRnkFirstSkin( )
    {
        if( this.getSkin( ) instanceof RnkFirstSkin )
            return ( RnkFirstSkin )this.getSkin( ) ;

        return null ;
    }

    public MCCompoundProperty getConfiguration( )
    {
        return configuration ;
    }

    public void setConfiguration( MCCompoundProperty appConfiguration )
    {
        this.configuration = appConfiguration ;
    }

    public void addEventToQueue( String name, Object obj, boolean deletePriors )
    {
        MMCDEvent event = getEventPool( ).getEvent( name ) ;
        assert event != null : "Trying to add null event" ;
        EventQueueElement eqe = new EventQueueElement( name, obj ) ;
        this.getEventQueue( ).addEventToQueue( eqe, event.getPriority( ), !deletePriors ) ;
    }

    public void insertUrgentEventToQueue( String name, Object obj, boolean deletePriors )
    {
        MMCDEvent event = getEventPool( ).getEvent( name ) ;
        assert event != null : "Trying to inserturgent null event" ;
        EventQueueElement eqe = new EventQueueElement( name, obj ) ;
        this.getEventQueue( ).insertUrgentEvent( eqe, event.getPriority( ), !deletePriors ) ;
    }

    public void init( InputWrapper iw ) throws MMCDException
    {
        try
        {
            // we only have a single property
            MCCompoundProperty cfgp = ( MCCompoundProperty )PropertyUtils.loadProperties( iw )[ 0 ] ;

            MCCompoundProperty cpjava = cfgp.getCompoundProperty( "@java" ) ;

            // do not set gui if not specified
            if( cpjava.getAtomicProperty( "$swingui" ) != null )
            {
                if( !"default".equals( cpjava.getAtomicProperty( "$swingui" ).getValue( ) ) )
                    UIManager.setLookAndFeel( cpjava.getAtomicProperty( "$swingui" ).getValue( ) ) ;
                else
                    UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName( ) ) ;
            }

            if( cpjava.getAtomicProperty( "$noxp" ) != null )
            {
                System.setProperty( "swing.noxp", cpjava.getAtomicProperty( "$noxp" ).getValue( ) ) ;
            }
            
            if( cpjava.getAtomicProperty( "$fullscreen" ) != null )
            {
                setIsfullscreen( true ) ;
            }

            if( cpjava.getAtomicProperty( "$use_system_window_decoration" ) != null )
                JFrame.setDefaultLookAndFeelDecorated( !cpjava.getAtomicProperty( "$use_system_window_decoration" ).getBooleanValue( ) ) ;

            setConfiguration( cfgp ) ;
            MCCompoundProperty cp = cfgp.getCompoundProperty( "@program" ) ;

            MCProperty p ;

            p = cp.getAtomicProperty( "$optionsfile" ) ;
            Options.getInstance( ).init( p ) ;

            setApplication( ( MMCDApplication )MMCDClassManager.getCustomClassInstance( cp.getAtomicProperty( "$application" ).getValue( ) ) ) ;
            setSkin( ( MMCDSkin )MMCDClassManager.getCustomClassInstance( cp.getAtomicProperty( "$skin" ).getValue( ) ) ) ;

            p = cp.getAtomicProperty( "$languagefile" ) ;
            Translator.getInstance( ).init( p.getInputWrapper( ) ) ;

            p = cp.getAtomicProperty( "$defaultlanguage" ) ;
            Translator.getInstance( ).setLanguage( p.getValue( ) ) ;

            if( Options.getInstance( ).getString( "language" ) != null )
                Translator.getInstance( ).setLanguage( Options.getInstance( ).getString( "language" ) ) ;

            p = cp.getAtomicProperty( "$widgetfile" ) ;
            getSkin( ).init( this, p ) ;

            p = cp.getAtomicProperty( "$listenerfile" ) ;
            getListenerPool( ).init( this, getSkin( ).getWidgetPool( ), PropertyUtils.loadCompoundProperties( new InputWrapper( p.getValue( 0 ), p.getBooleanValue( 1 ), p.getValue( 2 ) ) ) ) ;

            p = cp.getAtomicProperty( "$eventfile" ) ;
            getEventPool( ).init( PropertyUtils.loadCompoundProperties( new InputWrapper( p.getValue( 0 ), p.getBooleanValue( 1 ), p.getValue( 2 ) ) ) ) ;
            getEventHandlerPool( ).init( this, getEventPool( ) ) ;

            p = cp.getAtomicProperty( "$appconfig" ) ;
            getApplication( ).init( this, PropertyUtils.loadCompoundProperties( p.getInputWrapper( ) )[ 0 ] ) ;

            p = cp.getAtomicProperty( "$layoutfile" ) ;
            getApplication( ).buildMainWindow( this, PropertyUtils.loadCompoundProperties( new InputWrapper( p.getValue( 0 ), p.getBooleanValue( 1 ), p.getValue( 2 ) ) ) ) ;

            // getApplication( ).getMainWindow( ).addComponentListener( getSkin( ) ) ;

            if( isfullscreen )
                getApplication( ).getMainWindow( ).setUndecorated( true ) ;

            getSkin( ).getGlassPaneManager( ).installGlassPane( this, getApplication( ).getMainWindow( ), getSkin( ).getTimerThread( ) ) ;

            // getApplication( ).getMainWindow( ).validate( ) ;
            // getApplication( ).getMainWindow( ).getContentPane( ).validate( ) ;
            // getSkin( ).resizeRecursively( getApplication( ).getMainWindow( ) ) ;
            // getApplication( ).getMainWindow( ).pack( ) ;

            if( isfullscreen )
            {
                GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment( ).getDefaultScreenDevice( ) ;
                gd.setFullScreenWindow( getApplication( ).getMainWindow( ) ) ;
                origdisplaymode = gd.getDisplayMode( ) ;
                gd.setDisplayMode( new DisplayMode( cpjava.getAtomicProperty( "$fullscreen" ).getNumericValue( 0 ), cpjava.getAtomicProperty( "$fullscreen" ).getNumericValue( 1 ), cpjava.getAtomicProperty( "$fullscreen" ).getNumericValue( 2 ), DisplayMode.REFRESH_RATE_UNKNOWN ) ) ;
            }

            // getApplication( ).getMainWindow( ).validate( ) ;
            // getApplication( ).getMainWindow( ).getContentPane( ).validate( ) ;
            // getSkin( ).resizeRecursively( getApplication( ).getMainWindow( ).getContentPane( ) ) ;
            // getApplication( ).getMainWindow( ).validate( ) ;
            // getApplication( ).getMainWindow( ).getContentPane( ).validate( ) ;

            p = cp.getAtomicProperty( "$autoexecfile" ) ;
            if( p != null )
            {
                autoexecProps = PropertyUtils.loadCompoundProperties( p.getInputWrapper( ) )[ 0 ] ;
            }

            p = cp.getAtomicProperty( "$autofinalizefile" ) ;
            if( p != null )
            {
                autoFinalizeProps = PropertyUtils.loadCompoundProperties( p.getInputWrapper( ) )[ 0 ] ;
                Runtime.getRuntime( ).addShutdownHook( new Thread( new MMCDShutDownHook( this ) ) ) ;
            }

        }
        catch( Exception e )
        {
            System.err.println( "Error while initializing Engine" ) ;
            e.printStackTrace( ) ;
            throw new MMCDException( e ) ;
        }
    }

    public void startup( )
    {
        getSkin( ).startup( ) ;
        getApplication( ).startup( ) ;
        runAutoexec( ) ;
    }

    protected void runAutoexec( )
    {
        if( autoexecProps == null )
            return ;
        int i = 0 ;
        MCCompoundProperty cpevents = this.autoexecProps.getCompoundProperty( "@events" ) ;
        MCProperty p ;
        while( ( p = cpevents.getAtomicProperty( "$event_" + ( i++ ) ) ) != null )
        {
            String eventData = null ;
            if( p.getNumValues( ) > 1 )
                eventData = p.getValue( 1 ) ;
            MMCDEvent mce = getEventPool( ).getEvent( p.getValue( 0 ) ) ;
            EventQueueElement newevt = new EventQueueElement( mce.getName( ), eventData ) ;
            getEventQueue( ).addEventToQueue( newevt, mce.getPriority( ), true ) ;
        }
    }

    protected synchronized void runAutoFinalize( )
    {
        // şimdilik burada bitsin
        // this is for now..
        // Runtime.getRuntime( ).halt( 0 ) ;

        System.err.println( "finalizing.." ) ;
        if( autoFinalizeProps == null )
            return ;
        MCCompoundProperty cpevents = this.autoFinalizeProps.getCompoundProperty( "@events" ) ;
        autoFinalizeProps = null ;
        int i = 0 ;
        MCProperty p ;
        while( ( p = cpevents.getAtomicProperty( "$event_" + ( i++ ) ) ) != null )
        {
            String eventData = null ;
            if( p.getNumValues( ) > 1 )
                eventData = p.getValue( 1 ) ;
            MMCDEvent mce = getEventPool( ).getEvent( p.getValue( 0 ) ) ;
            EventQueueElement newevt = new EventQueueElement( mce.getName( ), eventData ) ;
            getEventQueue( ).addEventToQueue( newevt, mce.getPriority( ), true ) ;
        }

        // BURAYA HALT EVENTİ EKLENECEK
        //        EventQueueElement newevt = new EventQueueElement( "HALT", null ) ;
        //        getEventQueue( ).addEventToQueue( newevt, 1, true ) ;
        //        
        //        run( ) ;

        //        int exCnt = 0 ;
        //        while( getEventQueue( ).getEventCount( ) > 0 )
        //        {
        //            System.err.println( "Event cnt:" + getEventQueue( ).getEventCount( ) ) ;
        //            try
        //            {
        //                // handleNextEventBlocked( ) ;
        //                Thread.sleep( 5 ) ;
        //            }
        //            catch( Throwable t )
        //            {
        //                ++exCnt ;
        //                if( exCnt > 5 )
        //                    break ;
        //            }
        //        }
        //        isrunning = false ;
    }

    public void run( )
    {
        while( isrunning )
        {
            // System.err.println( "In handle loop.." ) ;
            try
            {
                handleNextEventBlocked( ) ;
            }
            catch( ThreadDeath dt )
            {
                System.err.println( "Thread is dead!" ) ;
                dt.printStackTrace( ) ;
                throw dt ;
            }
            catch( Exception e )
            {
                // do not exit..
                ThrowableHandler.getInstance( ).addThrowable( e, ThrowableHandler.S_ERROR ) ;
            }
            catch( Throwable t )
            {
                // newer exit
                ThrowableHandler.getInstance( ).addThrowable( t, ThrowableHandler.S_FATAL ) ;
            }
        }
        System.err.println( "Handling over.." ) ;
    }

    public synchronized void handleNextEventBlocked( ) throws MMCDException
    {
        EventQueueElement eqe = getEventQueue( ).getNextEventBlocked( getEventPool( ) ) ;
        long dispatchStart = System.currentTimeMillis() ;
        if( eqe.event.getIsSwing( ) )
        {
            if( eqe.event.getIsSyncronous( ) )
            {
                try
                {
                    getSkin( ).getSwingEventsQueue( ).addEventToQueue( eqe, eqe.event.getPriority( ) ) ;
                    SwingUtilities.invokeAndWait( getSkin( ) ) ;
                }
                catch( Exception e )
                {
                    throw new MMCDException( e ) ;
                }
            }
            else
            {
                try
                {
                    getSkin( ).getSwingEventsQueue( ).addEventToQueue( eqe, eqe.event.getPriority( ) ) ;
                    SwingUtilities.invokeLater( getSkin( ) ) ;
                }
                catch( Exception e )
                {
                    throw new MMCDException( e ) ;
                }
            }
        }
        else
        {
            //System.out.println( "Default Handler: eqe" + eqe.name ) ;

            if( eqe != null ) // maybe an exception is thrown..
            {
                if( eqe.event.getIsSyncronous( ) )
                    this.getSkin( ).setWaitingCursor( eqe.event.getMessage( ) ) ;

                eqe.event.getHandlerInstance( ).handleEvent( eqe ) ;

                if( eqe.event.getIsSyncronous( ) )
                    this.getSkin( ).setNormalCursor( ) ;
            }
            //System.out.println( "Default Handler done: eqe" + eqe.name ) ;
        }
        long dispatchEnd = System.currentTimeMillis( ) ;
        if( eqe != null )
            System.err.println( "Event <" + eqe.name + "> handled in " + ( dispatchEnd - dispatchStart ) + " ms." ) ;
    }

    public synchronized void handleRemainingEventsInQueue( ) throws MMCDException
    {
        EventQueueElement eqe = null ;
        do
        {
            eqe = getEventQueue( ).getNextEventNonBlocked( getEventPool( ) ) ;

            if( eqe != null ) // maybe an exception is thrown..
            {
                //System.out.println( "Default Handler: eqe" + eqe.name ) ;

                if( eqe.event.getIsSyncronous( ) )
                    this.getSkin( ).setWaitingCursor( eqe.event.getMessage( ) ) ;

                try
                {
                    eqe.event.getHandlerInstance( ).handleEvent( eqe ) ;
                }
                catch( ThreadDeath dt )
                {
                    System.err.println( "Thread is dead!" ) ;
                    dt.printStackTrace( ) ;
                    throw dt ;
                }
                catch( Exception e )
                {
                    // do not exit..
                    ThrowableHandler.getInstance( ).addThrowable( e, ThrowableHandler.S_ERROR ) ;
                }
                catch( Throwable t )
                {
                    // newer exit
                    ThrowableHandler.getInstance( ).addThrowable( t, ThrowableHandler.S_FATAL ) ;
                }

                if( eqe.event.getIsSyncronous( ) )
                    this.getSkin( ).setNormalCursor( ) ;

                //System.out.println( "Default Handler done: eqe" + eqe.name ) ;
            }

        }
        while( eqe != null ) ;
    }

    public MMCDEventQueue getEventQueue( )
    {
        return eventQueue ;
    }

    public void setEventQueue( MMCDEventQueue eventQueue )
    {
        this.eventQueue = eventQueue ;
    }

    public EventPool getEventPool( )
    {
        return eventPool ;
    }

    public void setEventPool( EventPool eventPool )
    {
        this.eventPool = eventPool ;
    }

    public MMCDListenerPool getListenerPool( )
    {
        return listenerPool ;
    }

    public void setListenerPool( MMCDListenerPool listenerPool )
    {
        this.listenerPool = listenerPool ;
    }

    public EventHandlerPool getEventHandlerPool( )
    {
        return eventHandlerPool ;
    }

    public void setEventHandlerPool( EventHandlerPool eventHandlerPool )
    {
        this.eventHandlerPool = eventHandlerPool ;
    }

    public boolean isIsfullscreen( )
    {
        return isfullscreen ;
    }

    public void setIsfullscreen( boolean isfullscreen )
    {
        this.isfullscreen = isfullscreen ;
    }

//    public void finalize( )
//    {
//        // doFinally( ) ;
//        System.err.println( "Finally engine is finalized!" ) ;
//    }

    public void doFinally( ) throws MMCDException
    {
        System.out.println( "shutting ..." ) ;
        runAutoFinalize( ) ;
        handleRemainingEventsInQueue( ) ;
        if( isfullscreen )
        {
            GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment( ).getDefaultScreenDevice( ) ;
            gd.setDisplayMode( origdisplaymode ) ;
            gd.setFullScreenWindow( null ) ;
            origdisplaymode = null ;
            isfullscreen = false ;
        }
        // Bu satır kitliyor olabilir..
        // freeAllResources( ) ;
        System.out.println( "has done last work ..." ) ;
    }

    public void freeAllResources( )
    {
        isrunning = false ;
        eventQueue.freeAllResources( ) ;
        eventQueue = null ;
        eventPool.getAllEvents( ).clear( ) ;
        eventPool = null ;
        listenerPool.getAllListeners( ).clear( ) ;
        listenerPool = null ;
        eventHandlerPool.getAllEventHandlers( ).clear( ) ;
        eventHandlerPool = null ;
        configuration = null ;
        getSkin( ).freeAllResources( ) ;
        setSkin( null ) ;
        getApplication( ).freeAllResources( ) ;
        setApplication( null ) ;

        if( isfullscreen )
        {
            GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment( ).getDefaultScreenDevice( ) ;
            gd.setDisplayMode( origdisplaymode ) ;
            gd.setFullScreenWindow( null ) ;
            origdisplaymode = null ;
            isfullscreen = false ;
        }

    }
}