content: /*
   * -----------------------------------------------------------------------
   * Copyright 2014 - Alistair Rutherford - www.netthreads.co.uk
   * -----------------------------------------------------------------------
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *     http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   * 
   */
  
  package com.netthreads.libgdx.director;
  
  import aurelienribon.tweenengine.Tween;
  
  import com.badlogic.gdx.Gdx;
  import com.badlogic.gdx.graphics.GL20;
  import com.badlogic.gdx.graphics.g2d.SpriteBatch;
  import com.badlogic.gdx.scenes.scene2d.Actor;
  import com.badlogic.gdx.scenes.scene2d.Group;
  import com.badlogic.gdx.utils.Disposable;
  import com.badlogic.gdx.utils.viewport.StretchViewport;
  import com.badlogic.gdx.utils.viewport.Viewport;
  import com.google.inject.Singleton;
  import com.netthreads.libgdx.event.ActorEvent;
  import com.netthreads.libgdx.event.ActorEventObserver;
  import com.netthreads.libgdx.event.ActorEventSource;
  import com.netthreads.libgdx.scene.Scene;
  import com.netthreads.libgdx.tween.ActorAccessor;
  import com.netthreads.libgdx.tween.GroupAccessor;
  
  /**
   * Scene director.
   * 
   * This class routes actor events around observers and updates the view. It also maintains global note of view width and
   * height.
   * 
   */
  @Singleton
  public class Director implements Disposable
  {
  	private static final float DEFAULT_CLEAR_COLOUR_RED = 0.0f;
  	private static final float DEFAULT_CLEAR_COLOUR_BLUE = 0.0f;
  	private static final float DEFAULT_CLEAR_COLOUR_GREEN = 0.0f;
  	private static final float DEFAULT_CLEAR_COLOUR_ALPHA = 1.0f;
  
  	private ActorEventSource eventSource;
  
  	private Viewport viewport;
  
  	private Scene scene;
  
  	private float scaleFactorX;
  	private float scaleFactorY;
  
  	private SpriteBatch spriteBatch;
  
  	private float clearColourR = DEFAULT_CLEAR_COLOUR_RED;
  	private float clearColourB = DEFAULT_CLEAR_COLOUR_BLUE;
  	private float clearColourG = DEFAULT_CLEAR_COLOUR_GREEN;
  	private float clearColourA = DEFAULT_CLEAR_COLOUR_ALPHA;
  
  	private static final int DEFAULT_WIDTH = 320;
  	private static final int DEFAULT_HEIGHT = 480;
  
  	/**
  	 * Construct Director elements.
  	 * 
  	 */
  	public Director()
  	{
  		scene = null;
  
  		// This required Graphics context.
  		spriteBatch = new SpriteBatch();
  
  		// Director maintains event source.
  		eventSource = new ActorEventSource();
  
  		// These are scale factors for adjusting touch events to the actual size
  		// of the view-port.
  		scaleFactorX = 1;
  		scaleFactorY = 1;
  
  		// All scenes share the same viewport.
  		viewport = null;
  
  		// Set up tween default configuration.
  		Tween.registerAccessor(Actor.class, new ActorAccessor());
  		Tween.registerAccessor(Group.class, new GroupAccessor());
  	}
  
  	/**
  	 * Update main loop.
  	 * 
  	 */
  	public void update()
  	{
  		// Update events.
  		eventSource.update();
  
  		// Update View
  		Gdx.gl.glClearColor(clearColourR, clearColourB, clearColourG, clearColourA);
  		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
  
  		if (scene != null)
  		{
  			float delta = Gdx.graphics.getDeltaTime();
  
  			scene.act(delta);
  
  			scene.draw();
  		}
  		else
  		{
  			Gdx.app.log("Director", "WTF! - No scene");
  		}
  	}
  
  	/**
  	 * Set the current scene.
  	 * 
  	 * @param scene
  	 */
  	public synchronized void setScene(Scene scene)
  	{
  		// If already active scene...
  		if (this.scene != null)
  		{
  			// Exit stage left..
  			this.scene.exit();
  		}
  
  		this.scene = scene;
  
  		if (this.scene != null)
  		{
  			// Enter stage right..
  			this.scene.enter();
  
  			// NOTE: Route input events to the scene.
  			Gdx.input.setInputProcessor(scene.getInputMultiplexer());
  		}
  
  	}
  
  	/**
  	 * Return currently running scene.
  	 * 
  	 * @return The current scene view.
  	 */
  	public synchronized Scene getScene()
  	{
  		return scene;
  	}
  
  	/**
  	 * Return scene width.
  	 * 
  	 * @return The width.
  	 */
  	public float getWidth()
  	{
  		return getViewport().getWorldWidth();
  	}
  
  	/**
  	 * Return scene height.
  	 * 
  	 * @return The height.
  	 */
  	public float getHeight()
  	{
  		return getViewport().getWorldHeight();
  	}
  
  	/**
  	 * Set display width/height.
  	 * 
  	 * We need to ensure this is done when a client application is created otherwise the default viewport will be used.
  	 * 
  	 * @param width
  	 * @param height
  	 */
  	public void setWidthHeight(int width, int height)
  	{
  		if (viewport == null)
  		{
  			viewport = new StretchViewport(width, height);
  		}
  
  		if (scene != null)
  		{
  			viewport.update(width, height, true);
  		}
  	}
  
  	/**
  	 * Adjust the scale factors for touch/mouse events to match the size of the stage.
  	 * 
  	 * @param width
  	 *            The new width.
  	 * @param height
  	 *            The new height.
  	 */
  	public void resize(int width, int height)
  	{
  		setWidthHeight(width, height);
  
  		float worldWidth = viewport.getWorldWidth();
  		float worldHeight = viewport.getWorldHeight();
  
  		scaleFactorX = (float) worldWidth / width;
  		scaleFactorY = (float) worldHeight / height;
  	}
  
  	/**
  	 * Send event to observers.
  	 * 
  	 * @param id
  	 *            The event id.
  	 * @param actor
  	 *            The associated actor.
  	 */
  	public void sendEvent(int id, Actor actor)
  	{
  		eventSource.sendEvent(id, actor, ActorEvent.DEFAULT_PRIORITY);
  	}
  
  	/**
  	 * Send event to observers.
  	 * 
  	 * @param id
  	 *            The event id.
  	 * @param actor
  	 *            The associated actor.
  	 * @param priority
  	 *            Adopt a priority value. Must be > 0.
  	 */
  	public void sendEvent(int id, Actor actor, int priority)
  	{
  		eventSource.sendEvent(id, actor, priority);
  	}
  
  	/**
  	 * Add event observer event handler.
  	 * 
  	 * DO NOT PUT THIS INTO THE CONSTRUCTOR. IT MUST GO INTO THE "ENTER" HANDLER.
  	 * 
  	 * @param observer
  	 *            The event observer.
  	 */
  	public void registerEventHandler(ActorEventObserver observer)
  	{
  		eventSource.addObserver(observer);
  	}
  
  	/**
  	 * Remove event observer event handler.
  	 * 
  	 * DO NOT FORGET TO PUT THIS INTO THE "EXIT" HANDLER IF YOU HAVE MATCHING "REGISTER" IN THE ENTER HANDLER.
  	 * 
  	 * @param observer
  	 *            The event observer.
  	 */
  	public void deregisterEventHandler(ActorEventObserver observer)
  	{
  		eventSource.removeObserver(observer);
  	}
  
  	/**
  	 * Clear all handlers.
  	 * 
  	 */
  	public void clearEventHandlers()
  	{
  		// Clear all event subscriptions.
  		eventSource.clear();
  	}
  
  	/**
  	 * Return scale factor for touch/mouse events.
  	 * 
  	 * @return The x scale factor.
  	 */
  	public float getScaleFactorX()
  	{
  		return scaleFactorX;
  	}
  
  	/**
  	 * Return scale factor for touch/mouse events.
  	 * 
  	 * @return The y scale factor.
  	 */
  	public float getScaleFactorY()
  	{
  		return scaleFactorY;
  	}
  
  	public SpriteBatch getSpriteBatch()
  	{
  		return spriteBatch;
  	}
  
  	public void setSpriteBatch(SpriteBatch spriteBatch)
  	{
  		this.spriteBatch = spriteBatch;
  	}
  
  	@Override
  	public void dispose()
  	{
  		spriteBatch.dispose();
  	}
  
  	public void setEventSource(ActorEventSource eventSource)
  	{
  		this.eventSource = eventSource;
  	}
  
  	public void setClearColourR(float clearColourR)
  	{
  		this.clearColourR = clearColourR;
  	}
  
  	public void setClearColourB(float clearColourB)
  	{
  		this.clearColourB = clearColourB;
  	}
  
  	public void setClearColourG(float clearColourG)
  	{
  		this.clearColourG = clearColourG;
  	}
  
  	public void setClearColourA(float clearColourA)
  	{
  		this.clearColourA = clearColourA;
  	}
  
  	/**
  	 * We have to create a dummy viewport if one doesn't exist or things will crash.
  	 * 
  	 * @return The current viewport.
  	 */
  	public Viewport getViewport()
  	{
  		if (viewport == null)
  		{
  			viewport = new StretchViewport(DEFAULT_WIDTH, DEFAULT_HEIGHT);
  		}
  
  		return viewport;
  	}
  
  }
  