/*
 * Copyright (c) 2006-2008 MiniMe. Code released under The MIT/X Window System
 * License. Full license text can be found in license.txt
 */
package minime.core;

import java.util.Stack;
import java.util.Vector;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Canvas;
import javax.microedition.midlet.MIDlet;

import minime.Drawable;
import minime.GraphicsUtil;
import minime.Logger;
import minime.Portability;
import minime.ui.View;

/**
 * The Runtime class controls the UI view, including display the current view,
 * change context views.	
 * 
 * @author aaa
 * 
 */
public class Runtime
{
    private Stack viewsStack;
    private View currentView; // currentView is always on top of the stack

    private Displayable displayable;
    private MIDlet midlet;
    private int displayWidth;
    private int displayHeight;
    private int top;
    private int left;
    private boolean firstTimeClearScreen = false ;
    static final Logger LOG = Logger.getLogger("minime.Runtime");
    
    static private Runtime instance = null;

    /**
     * Use static factory method to return a Runtime reference instance.
     * 
     * @return a Runtime reference instance.
     */
    public static Runtime getInstance()
    {
        if (instance == null)
        {
            throw new RuntimeException("Runtime::bootstrap has not been called yet");
        }
        return instance;
    }

    private Runtime(MIDlet midlet)
    {
        this.midlet = midlet;
        viewsStack = new Stack();
    }

    /**
     * Initializes Runtime instance the first time it's called.
     * 
     * @param midlet
     *            the midlet instance.
     */
    public static void bootstrap(MIDlet midlet)
    {
        if (instance != null)
        {
            throw new RuntimeException("Runtime::bootstrap called more than once");
        }
        instance = new Runtime(midlet);

        // bootstrap other services
        EventDispatcher.bootstrap();
    }

    protected void gameTick()
    {
        if (currentView != null)
        {
        	// layout, then render
        	currentView.gameTick() ;
        }
    	
    }
    protected void paint(Graphics gc)
    {
    	if (!firstTimeClearScreen)
    	{
    		// clear the screen
	    	javax.microedition.lcdui.Displayable nativeDisplayable = displayable.getNativeDisplayable();
	    	gc.fillRect(0, 0, nativeDisplayable.getWidth() ,nativeDisplayable.getHeight());
//	    	// set the clipping rec
//	    	gc.setClip(getLeft(), getTop(), getDisplayWidth(), getDisplayHeight()) ;
	    	firstTimeClearScreen = true ;
    	}
	    	
    	GraphicsUtil.setClip(gc,getLeft(), getTop(), getDisplayWidth(), getDisplayHeight()) ;
        renderDrawables(gc);
    }

    public void repaint()
    {
//        if (displayable != null)
//        {
//            displayable.repaint();
//        }
    }

    public int getDisplayWidth()
    {
        return displayWidth;
    }

    public int getDisplayHeight()
    {
        return displayHeight;
    }

    //
    // Drawables management
    //
    private void renderDrawables(Graphics gc)
    {
        if (currentView != null)
        {
        	// layout, then render
            currentView.layout() ;
            currentView.render(gc);
        }
    }

    /**
     * Go to the new view without pushing it into the stack.
     * 
     * @param view
     *            new view to be displayed
     * @throws NullPointerException
     *             if view is null
     */
    public void gotoView(View view) throws NullPointerException
    {
        if (view == null)
            throw new NullPointerException();

        // deactivate the current view
        if (currentView != null)
            currentView.deactivate();

        currentView = view;
        currentView.activate();
    }

    /**
     * Push a view in the stack and set it as current screen.
     * 
     * @param view
     *            new view to be displayed
     * @throws NullPointerException
     *             if view is null
     */
    public void pushView(View view) throws NullPointerException
    {
        if (view == null)
            throw new NullPointerException();

        // deactivate the current view
        if (currentView != null)
            currentView.deactivate();

        // activate a push the new view
        LOG.debug("Add View:" + view);
        view.activate();
        viewsStack.push(view);
        // new view is now the current view
        currentView = view;
    }

    /**
     * Perform a back action, go back to the previous view.
     * 
     * This function pop the top of the view stack and set as current the new
     * top of the stack. If the stack is empty, quit the application.
     * 
     */
    public View popView()
    {
        View view = null;

        // desactivate the current view and pop it from the top of the stack
        currentView.deactivate();
        if (viewsStack.empty())
        {
            Minilet.inst.destroyApp(false);
            midlet.notifyDestroyed();
            return null;
        }
        else
        {
            view = (View) viewsStack.pop();
            LOG.debug("Remove View:" + view);
            if (viewsStack.empty())
            {
                Minilet.inst.destroyApp(false);
                midlet.notifyDestroyed();
                instance = null;
            }
            else
            {
                // next view on the top of the stack become the currentView and
                // get activated
                currentView = (View) viewsStack.peek();
                currentView.activate();
            }

            return view;
        }
    }

    /**
     * clean viewsStack
     */
    public void cleanViews()
    {
        viewsStack = new Stack();
    }
    
    public Displayable getMiniDisplayable()
    {
        return displayable;
    }

    public void setDisplayable(Displayable displayable)
    {
        LOG.debug("set Displayable:" + displayable);
        this.displayable = displayable;
        javax.microedition.lcdui.Displayable nativeDisplayable = displayable.getNativeDisplayable();
        Display.getDisplay(midlet).setCurrent(nativeDisplayable);
        minime.Portability.setCanvas((Canvas) displayable.getCanvas());
        if (null == nativeDisplayable)
        {
            displayWidth = 0;
            displayHeight = 0;
            top = 0;
            left = 0;
        }
        else
        {
            int nativeWidth = nativeDisplayable.getWidth();
            int nativeHeight = nativeDisplayable.getHeight();
            
//#ifdef ${screen.type.large}
    		displayWidth = ${screen.large.width};
        	displayHeight = ${screen.large.height};
//#elifdef  ${screen.type.high}
        	displayWidth = ${screen.high.width};
        	displayHeight = ${screen.high.height};
//#elifdef  ${screen.type.middle}
        	displayWidth = ${screen.middle.width};
        	displayHeight = ${screen.middle.height};
//#elifdef  ${screen.type.small}
        	displayWidth = ${screen.small.width};
            displayHeight = ${screen.small.height};
//#else
            displayWidth  = ${device.screen.width};
            displayHeight = ${device.screen.height};
//#endif
       
        	if(nativeWidth<displayWidth)
        	{
        	     displayWidth=nativeWidth;
        	}
        	if(nativeHeight<displayHeight)
        	{
        		displayHeight=nativeHeight; 
        	}
        	top  = (nativeHeight - displayHeight)/2;
        	left = (nativeWidth - displayWidth)/2;
        }
    }

	public int getLeft() {
		return left;
	}
	public void setLeft(int left) {
		this.left = left;
	}
	public int getTop() {
		return top;
	}
	public void setTop(int top) {
		this.top = top;
	}
}
