/*
 * GameMode.java
 *
 * This is the primary class file for running the game.  You should study this file for
 * ideas on how to structure your own root class. This class follows a 
 * model-view-controller pattern fairly strictly.
 *
 * Author: Walker M. White
 * Based on original Parallax Sample by Don Holden, 2007
 * LibGDX version, 2/28/2015
 */
package edu.cornell.gdiac.parallax;

import com.badlogic.gdx.utils.JsonValue;
import edu.cornell.gdiac.assets.AssetDirectory;
import edu.cornell.gdiac.util.*;

import com.badlogic.gdx.*;
import com.badlogic.gdx.assets.*;
import com.badlogic.gdx.graphics.*;

/**
 * The primary controller class for the game.
 *
 * While GDXRoot is the root class, it delegates all of the work to the player mode
 * classes. This is the player mode class for running the game. In initializes all 
 * of the other classes in the game and hooks them together.  It also provides the
 * basic game loop (update-draw).
 */
public class GameMode implements Screen {
	// GRAPHICS
	// Asset loading is handled statically, so these are static variables
	/** The absolute background layer */
	private Texture sunsetTexture;
	/** The mountains in front of the sunset */
	private Texture mountainTexture;
	/** The road underneath our wheels */
	private Texture roadTexture;
	/** The grass layer */
	private Texture grassTexture;
	/** The car wheels */
	private Texture wheelTexture;
	/** The car body */
	private Texture chassisTexture;
	
	/** The data constants */
	private JsonValue constants;

	/** Reference to drawing context to display graphics (VIEW CLASS) */
	private GameCanvas canvas;

	/** Reference to the underlying asset directory */
	private AssetDirectory directory;

	/** Reads input from keyboard or game pad (CONTROLLER CLASS) */
	private InputController inputController;
	
	/** Whether or not this player mode is still active */
	private boolean active;
	/** Listener that will update the player mode when we are done */
	private ScreenListener listener;

	/** Vehicle state */
    private float carPosition;
    private float wheelRotation;
    private float bumpSine;
    
	/**
	 * Creates a new game with the given drawing context.
	 *
	 * This constructor initializes the models and controllers for the game.  The
	 * view has already been initialized by the root class.
	 */
	public GameMode(GameCanvas canvas) {
		this.canvas = canvas;
		// Create the controllers.
		inputController = new InputController();
	}
	
	/**
	 * Gather the assets for this game.
	 *
	 * This method extracts the asset variables from the given asset directory. It
	 * should only be called after the asset directory is completed.
	 *
	 * @param directory	Reference to global asset manager.
	 */
	public void gatherAssets(AssetDirectory directory) {
		// Access the assets used directly by this controller
		this.directory = directory;
		grassTexture = directory.getEntry("grass",Texture.class);
		roadTexture  = directory.getEntry("road",Texture.class);
		sunsetTexture = directory.getEntry("sunset",Texture.class);
		mountainTexture = directory.getEntry("mountain",Texture.class);
		wheelTexture = directory.getEntry("wheels",Texture.class);
		chassisTexture = directory.getEntry("chassis",Texture.class);
		
		constants = directory.getEntry("constants",JsonValue.class);
	}

	
	/**
	 * Dispose of all (non-static) resources allocated to this mode.
	 */
	public void dispose() {
		inputController = null;
		canvas = null;
	}
	
	
	/**
	 * Update the game state.
	 *
	 * We prefer to separate update and draw from one another as separate methods, instead
	 * of using the single render() method that LibGDX does.  We will talk about why we
	 * prefer this in lecture.
	 *
	 * @param delta Number of seconds since last animation frame
	 */
	private void update(float delta) {
		inputController.readInput();

		// Allows the game to exit
		if (inputController.didExit()) {
			System.exit(0);
		}

        // Change position of car
		carPosition += inputController.getSpeed();
		bumpSine += inputController.getSpeed() * constants.get("bumpiness").getFloat("frequency",0);

		// Change rotation in degrees
		float FULLCIRCLE = 360.0f;
		wheelRotation -= (inputController.getSpeed()*FULLCIRCLE) / (Math.PI*wheelTexture.getWidth());
		if (wheelRotation > FULLCIRCLE) {
			wheelRotation -= FULLCIRCLE;
		} else if (wheelRotation < 0.0f) {
			wheelRotation += FULLCIRCLE;
		}
	}
	
	/**
	 * Draw the status of this player mode.
	 *
	 * We prefer to separate update and draw from one another as separate methods, instead
	 * of using the single render() method that LibGDX does.  We will talk about why we
	 * prefer this in lecture.
	 */
	private void draw(float delta) {
		canvas.begin();

        // Camera's X-position, 400 pixels (half the screen) to the left of the car.
		float camera = -carPosition;

        // Draw the sunset
		canvas.drawWrapped(sunsetTexture, constants.get("parallax").getFloat("sunset",0) * camera, 
						   constants.get("height").getInt("sunset",0));

        // Draw the mountains
		canvas.drawWrapped(mountainTexture, constants.get("parallax").getFloat("mountains",0) * camera, 
						   constants.get("height").getInt("mountains",0));

        // Draw the background grass
		canvas.drawWrapped(grassTexture, constants.get("parallax").getFloat("backgrass",0) * camera, 
						   constants.get("height").getInt("backgrass",0));

        // Draw the road
		canvas.drawWrapped(roadTexture, constants.get("parallax").getFloat("road",0) * camera, 
						   constants.get("height").getInt("road",0));

        // Draw the wheels
        float ox = wheelTexture.getWidth()/2.0f;
        float oy = wheelTexture.getHeight()/2.0f;
        
        float offset;
        offset =  constants.get("parallax").getFloat("road",0) * constants.get("offset").getFloat("wheel1",0);
		canvas.draw(wheelTexture, Color.WHITE, ox, oy, offset, constants.get("height").getInt("wheel",0),
					wheelRotation, 1.0f, 1.0f);
        offset =  constants.get("parallax").getFloat("road",0) * constants.get("offset").getFloat("wheel2",0);
		canvas.draw(wheelTexture, Color.WHITE, ox, oy, offset, constants.get("height").getInt("wheel",0),
					wheelRotation, 1.0f, 1.0f);

        // Draw the car
        ox = chassisTexture.getWidth()/2.0f;
        oy = chassisTexture.getHeight()/2.0f;
        float bump = constants.get("bumpiness").getFloat("amplitude",0) * (float)Math.sin(bumpSine);
       	offset =  constants.get("parallax").getFloat("road",0) * constants.get("offset").getFloat("car",0);
		canvas.draw(chassisTexture, Color.WHITE, ox, oy, offset, constants.get("height").getInt("car",0)+bump,
					0.0f, 1.0f, 1.0f);
					
        // Draw the foreground grass
		canvas.drawWrapped(grassTexture, constants.get("parallax").getFloat("foregrass",0) * camera, 
						   constants.get("height").getInt("foregrass",0));
		canvas.end();		
	}
	
	/**
	 * Called when the Screen is resized. 
	 *
	 * This can happen at any point during a non-paused state but will never happen 
	 * before a call to show().
	 *
	 * @param width  The new width in pixels
	 * @param height The new height in pixels
	 */
	public void resize(int width, int height) {
		// IGNORE FOR NOW
	}

	/**
	 * Called when the Screen should render itself.
	 *
	 * We defer to the other methods update() and draw().  However, it is VERY important
	 * that we only quit AFTER a draw.
	 *
	 * @param delta Number of seconds since last animation frame
	 */
	public void render(float delta) {
		if (active) {
			update(delta);
			draw(delta);
			if (inputController.didExit() && listener != null) {
				listener.exitScreen(this, 0);
			}
		}
	}

	/**
	 * Called when the Screen is paused.
	 * 
	 * This is usually when it's not active or visible on screen. An Application is 
	 * also paused before it is destroyed.
	 */
	public void pause() {
		// TODO Auto-generated method stub
	}

	/**
	 * Called when the Screen is resumed from a paused state.
	 *
	 * This is usually when it regains focus.
	 */
	public void resume() {
		// TODO Auto-generated method stub
	}
	
	/**
	 * Called when this screen becomes the current screen for a Game.
	 */
	public void show() {
		// Useless if called in outside animation loop
		active = true;
	}

	/**
	 * Called when this screen is no longer the current screen for a Game.
	 */
	public void hide() {
		// Useless if called in outside animation loop
		active = false;
	}

	/**
	 * Sets the ScreenListener for this mode
	 *
	 * The ScreenListener will respond to requests to quit.
	 */
	public void setScreenListener(ScreenListener listener) {
		this.listener = listener;
	}
}
