using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class World : MonoBehaviour {

	private Game game;

	public Rhino rhinoPrefab; // inspector
	public Owl owlPrefab; // inspector
	public Poacher poacherPrefab; // inspector
	public RampagingRhino madRhinoPrefab; // inspector

	public Treasure treasurePrefab; // inspector
	public Food foodPrefab; // inspector
	public Transform groundPrefab; // inspector

	public Rhino rhino;
	private Owl owl;

	private FoodPoint[] foodPoints;
	private TreasurePoint[] treasurePoints;

	private List<Food> foods = new List<Food>();
	private List<Treasure> treasures = new List<Treasure>();

	private float nextPoacherTime = 0.0f;

	private UILabel treasureCountLabel;
	private UILabel healthLabel;
	
	public void Init (Game game, bool splitScreen) {
		Debug.Log ("World Start");
		this.game = game;
		
		// spawn the rhino
		rhino = Instantiate (rhinoPrefab) as Rhino;
		rhino.Init (this);
		if (splitScreen) {
			owl = Instantiate (owlPrefab) as Owl;
			owl.init (this);
		}
		
		// get all spawn points
		foodPoints = Object.FindObjectsOfType (typeof(FoodPoint)) as FoodPoint[];
		treasurePoints = Object.FindObjectsOfType (typeof(TreasurePoint)) as TreasurePoint[];
		
		// disable them all to make their renders disappear
		for (int i = 0; i < foodPoints.Length; ++i) {
			foodPoints [i].gameObject.SetActive (false);
		}
		for (int i = 0; i < treasurePoints.Length; ++i) {
			treasurePoints [i].gameObject.SetActive (false);
		}

		// set up all the poacher points
		PoacherPoint[] poacherPoints = Object.FindObjectsOfType (typeof(PoacherPoint)) as PoacherPoint[];
		for (int i = 0; i < poacherPoints.Length; ++i) {
			PoacherPoint p = poacherPoints[i];
			p.init (rhino);
		}
		
		// spawn a bunch of grounds
		/*for (float x = -1000; x <= 1000; x += groundPrefab.localScale.x) {
			for (float y = -1000; y <= 1000; y += groundPrefab.localScale.y) {
				Transform ground = Instantiate (groundPrefab) as Transform;
				ground.position = new Vector2 (x, y);
			}
		}*/

		Debug.Log ("World init");
		this.game = game;
		if (splitScreen) {
			SetupCameraViewpointsSplitScreen ();
		} else {
			SetupCameraViewpointRhino ();
		}

		treasureCountLabel = game.treasureCountLabel;
		healthLabel = game.healthLabel;
	}

	void Update() {
		float dt = Time.deltaTime;

		treasureCountLabel.text = "Rhinos freed: " + rhino.treasures;
		healthLabel.text = "Food: " + Mathf.RoundToInt ((rhino.hunger) * 100);

		// Try to spawn food
		if (foods.Count < Data.foodMax) {
			IEnumerable<Vector3> possiblePositions = foodPoints.Select(item => item.transform.position);
			IEnumerable<Vector3> notAllowedPositions = foods.Select(item => item.transform.position);
			notAllowedPositions = notAllowedPositions.Concat (new[] {rhino.transform.position});
			Vector3 point = findRandomPointFarFrom(possiblePositions, notAllowedPositions);
			if(point != Vector3.zero ) {
				Food food = Instantiate (foodPrefab) as Food;
				food.transform.position = point;
				food.Init();
				food.transform.Translate (new Vector3(0f,5f,0f));
				foods.Add (food);
			}
		}

		if (treasures.Count < 1) {
			IEnumerable<Vector3> possiblePositions = treasurePoints.Select(item => item.transform.position);
			IEnumerable<Vector3> notAllowedPositions = treasures.Select(item => item.transform.position);
			notAllowedPositions = notAllowedPositions.Concat (new[] {rhino.transform.position});
			Vector3 point = findRandomPointFarFrom(possiblePositions, notAllowedPositions);
			if(point != Vector3.zero ) {
				Treasure treasure = Instantiate (treasurePrefab) as Treasure;
				treasure.transform.position = point;
				treasure.transform.position = new Vector3(treasure.transform.position.x, treasure.transform.position.y + 5, treasure.transform.position.z);
				//treasure.transform.Translate (new Vector3(0f,5f,0f));
				treasure.Init();
				treasures.Add (treasure);
			}
		}
	}

	public void FixedUpdate() {

		// spawn a random mad rhino
		if (Random.value <= 0.001f) {
			RampagingRhino madRhino = Instantiate (madRhinoPrefab) as RampagingRhino;
			madRhino.target = rhino.transform;
			float distance = 300.0f;
			float angle = Random.Range (0, 2.0f * Mathf.PI);
			madRhino.init (new Vector3(distance * Mathf.Cos(angle), 0.0f, distance * Mathf.Sin (angle)));
		}
	}

	private Vector3 findRandomPointFarFrom(IEnumerable<Vector3> allowed, IEnumerable<Vector3> notAllowed) {
		IEnumerable<Vector3> points = allowed.Where ((a,index) => !isCloseToAny(a, notAllowed));
		if (points.Count() == 0) {
			Debug.Log ("No place to spawn :(");
			return Vector3.zero;
		}
		return points.ElementAt(Random.Range(0,points.Count()));
	}

	private bool isCloseToAny(Vector3 v, IEnumerable<Vector3> points) {
		return points.Any (n => (n - v).magnitude < Data.foodMinDistance);
	}

	public void SetupCameraViewpointsSplitScreen() {
		rhino.camera.transform.parent = null;
		rhino.camera.gameObject.AddComponent<RhinoFollower>();
		rhino.camera.GetComponent<RhinoFollower>().init(rhino);
		rhino.camera.rect = new Rect (0f, 0f, 0.5f, 1f);
		//owl.camera.transform.parent = null;
		owl.gameObject.AddComponent<FirstPersonOwlCamera>();
		owl.camera.rect = new Rect (0.5f, 0f, 0.5f, 1f);
		owl.transform.position = new Vector3(rhino.transform.position.x, Data.owlHeight, rhino.transform.position.z);
	}
	
	public void SetupCameraViewpointRhino() {
		rhino.camera.transform.parent = null;
		rhino.camera.gameObject.AddComponent<RhinoFollower>();
		rhino.camera.GetComponent<RhinoFollower>().init(rhino);
		rhino.camera.rect = new Rect (0f, 0f, 1f, 1f);
	}

	public void RemoveFood(Food food) {
		foods.Remove (food);
		Destroy (food.gameObject);
	}
	
	public int RemoveTreasure(Treasure treasure) {
		treasures.Remove (treasure);
		Destroy (treasure.gameObject);
		int rhinos = treasure.destroy ();
		return rhinos;
	}

	public Rhino GetRhino() {
		return rhino;
	}

	public Owl GetOwl() {
		return owl;
	}

}
