using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class StarMapState : SpaceStateController
{

	public static StarMapState instance = null;
	[SerializeField] GameObject starObject = null; // Set from editor - star object
	[SerializeField] Material routeMaterial = null; // Set from editor - star route material
	[SerializeField] Camera playerCamera = null; // Set from editor - main camera for mouse raytrace
	[SerializeField] GameObject currentSystemMarker = null; // Set from editor - current player position
	[SerializeField] GameObject selectedSystemMarker = null; // Set from editor - current player position
	[SerializeField] Color reachableStarColor = Color.green; // Set from editor - color of reachable star
	[SerializeField] Color unreachableStarColor = Color.red; // Set from editor - color of reachable star

	class CashedStar
	{
		public SphereCollider collider;
		public bool used;
		public int id;
	}

	List<CashedStar> starsPool = new List<CashedStar>();

	List<LineRenderer> routes = new List<LineRenderer> ();
	const float weight = 0.05f;
	int selectedStar = -1;

	public override void OnStateShow (bool fromBigger)
	{
		EndHyperJump ();
		CameraController.SetPosition(StarMap.Stars[Player.info.StarId].position, false);
		CameraController.SetNeedDistance (7f);
		CameraController.SetBorders (minCameraDistance + 1, minCameraDistance, maxCameraDistance, cameraMinYAngle, cameraMaxYAngle);
	}

	public void MakeHyperJump()
	{
		StarMapUI.ShowHyperJumpButton(false);
		if (IsCanReachStar(Player.info.StarId, selectedStar)) {
			Player.info.StarId = selectedStar;
			Player.info.currentObjectId = 0;
			if (currentSystemMarker != null) {
				Timer.Add (0.06f, (anim) => {
					currentSystemMarker.transform.localScale = Vector3.one * (1f - anim * anim);
				}, () => {
					currentSystemMarker.transform.localScale = Vector3.zero;
				});
			}
			HideRoutes();
			CameraController.SetPosition(StarMap.Stars[Player.info.StarId].position);
			Timer.Add (0.6f, (anim) => {

			}, () => {
				EndHyperJump();
			});
		}
	}

	void EndHyperJump()
	{
		MarkNearestStars (Player.info.StarId, Player.info.jumpDistance);
		MarkCurrentStar (Player.info.StarId);
		SelectStar ();
//		StarSystemState.Generate ();
		StarMapUI.ShowHyperJumpButton(false);
		RegenerateStars ();
	}

	void Awake ()
	{
		instance = this;
	}

	void Start ()
	{
		EndHyperJump ();
	}

	void RegenerateStars()
	{
		// Find nearest stars
		const float showDistance = 40f;
		List<int> showedStars = new List<int> ();
		Vector3 centerPoint = StarMap.Stars [Player.info.StarId].position;
		for (int i = 0; i < StarMap.Stars.Count; i++)
		{
			if ((StarMap.Stars[i].position - centerPoint).sqrMagnitude < showDistance * showDistance)
			{
				showedStars.Add(i);
			}
		}
		// disable not used stars
		foreach (var star in starsPool)
		{
			if (showedStars.Contains(star.id))
			{
				showedStars.Remove(star.id);
				if (!star.used)
				{
					star.collider.enabled = true;
				}
				star.used = true;
			}
			else
			{
				star.used = false;
				star.collider.enabled = false;
			}
		}
		//reuse old
		foreach (var star in starsPool)
		{
			if (!star.used)
			{
				if (showedStars.Count == 0)
				{
					break;
				}
				star.collider.transform.position = StarMap.Stars[showedStars[0]].position;
				star.collider.enabled = true;
				showedStars.Remove(star.id);
			}
		}
		// add new
		if (showedStars.Count > 0)
		{
			foreach (var starId in showedStars)
			{
				starsPool.Add(new CashedStar() {
					id = starId,
					used = true,
					collider = LoadStar(StarMap.Stars[starId].position)
				});
			}
		}
	}

	SphereCollider LoadStar(Vector3 position)
	{
		GameObject star = (GameObject)Instantiate((Object)starObject);
		star.SetActive(true);
		star.transform.parent = transform;
		star.transform.position = position;
		return star.GetComponent<SphereCollider> ();
	}

	bool IsCanReachStar(int currentStarIndex, int needStarIndex)
	{
		return (StarMap.Stars [currentStarIndex].position - StarMap.Stars [needStarIndex].position).sqrMagnitude < Player.info.jumpDistance * Player.info.jumpDistance;
	}

	void MarkNearestStars (int startStarIndex, float distance = 10)
	{
		HideRoutes ();
		for (int i = 0; i < StarMap.Stars.Count; i++) {
			if (i != startStarIndex) {
				if (IsCanReachStar(startStarIndex, i)) { // mark star as reachable
					ShowRoute (StarMap.Stars [startStarIndex].position, StarMap.Stars [i].position);
				}
			}
		}
	}

	void SelectStar (int starIndex = -1)
	{
		if (starIndex < 0) { // hide selector
			if (selectedSystemMarker != null) {
				selectedSystemMarker.SetActive (false);
			}
			StarMapUI.ShowHyperJumpButton(false);
		} else {
			selectedStar = starIndex;
			if (selectedSystemMarker != null) {
				selectedSystemMarker.SetActive (true);
				selectedSystemMarker.transform.position = StarMap.Stars [starIndex].position;
				selectedSystemMarker.transform.localScale = Vector3.zero;
				Timer.Add (0.2f, (anim) => {
					selectedSystemMarker.transform.localScale = Vector3.one * (anim * anim);
				}, () => {
					selectedSystemMarker.transform.localScale = Vector3.one;
				});
				SpriteRenderer sprite = selectedSystemMarker.GetComponent<SpriteRenderer>();
				if (IsCanReachStar(Player.info.StarId, starIndex))
				{
					sprite.color = reachableStarColor;
					StarMapUI.ShowHyperJumpButton();
				} else {
					sprite.color = unreachableStarColor;
					StarMapUI.ShowHyperJumpButton(false);
				}
			}
		}
	}

	void MarkCurrentStar (int starIndex)
	{
		if (currentSystemMarker != null) {
			currentSystemMarker.transform.position = StarMap.Stars [starIndex].position;
			Timer.Add (0.14f, (anim) => {
				currentSystemMarker.transform.localScale = Vector3.one * (anim * anim);
			}, () => {
				currentSystemMarker.transform.localScale = Vector3.one;
			});
		}
	}

	void HideRoutes ()
	{
		foreach (var route in routes) {
			if (route != null) {
				route.gameObject.SetActive (false);
			}
		}
	}

	void ShowRoute (Vector3 start, Vector3 end)
	{
		for (int i = 0; i < routes.Count; i++) {
			if (!routes [i].gameObject.activeSelf) {
				routes [i].gameObject.SetActive (true);
				routes [i].SetPosition (0, start);
				routes [i].SetPosition (1, end);
				return;
			}
		}
		LineRenderer route = Utils.Create<LineRenderer> (transform, "star_route");
		route.material = routeMaterial;
		route.SetPosition (0, start);
		route.SetPosition (1, end);
		route.SetWidth (weight, weight);
		route.useWorldSpace = false;

		routes.Add (route);
	}

	void Update ()
	{
		TracePlayerTouch ();
	}

	void TracePlayerTouch()
	{
		if (playerCamera == null)
		{
			Debug.LogError("StarMapController " + name + " does not set player camera");
			return;
		}
		if (!EventSystem.current.IsPointerOverGameObject ())
		{
			if (Input.GetMouseButtonUp (0))
			{
				RaycastHit hitInfo;
				if (Physics.Raycast (playerCamera.ScreenPointToRay (Input.mousePosition), out hitInfo, 50f))
				{
					if (hitInfo.collider != null)
					{
						foreach (var star in starsPool)
						{
							if (hitInfo.collider == star.collider)
							{
								SelectStar (star.id);
								break;
							}
						}
					}
				}
			}
		}
	}

	#if UNITY_EDITOR
	int mod = 0;
	float time = 0;
	void OnDrawGizmos()
	{
		if (Application.isPlaying)
		{
			time -= Time.deltaTime;
			if (time < 0) {
				time = 0.5f;
				mod = (mod + 1) % 10;
			}
			for (int i = 0; i < StarMap.Stars.Count; i++)
			{
				if (i % 10 == mod)
				{
					Gizmos.DrawSphere(StarMap.Stars[i].position, 0.3f);	
				}
			}
		}
	}
	#endif
}
