﻿using System;
using System.Collections.Generic;
using UnityEngine;

public class PlacementManager : MonoBehaviour
{
	public float placementRotation = 0f;
	public PlaceableTypes selectedPlacement = PlaceableTypes.Harvester;

	private bool placeable;
	private Vector3 placeableCenter;
	private Vector3 placeableSize;
	private Dictionary<Tuple<PlaceableTypes, bool>, GameObject> placementObjects = new Dictionary<Tuple<PlaceableTypes, bool>, GameObject>();

	private void Start()
	{
		Vector3 position = Vector3.up * 1000;
		Quaternion rotation = Quaternion.Euler(Vector3.up * placementRotation);
		placementObjects.Add(new Tuple<PlaceableTypes, bool>(PlaceableTypes.Harvester, true), Instantiate(GameManager.TurretPrefab, position, rotation));
		placementObjects.Add(new Tuple<PlaceableTypes, bool>(PlaceableTypes.Harvester, false), Instantiate(GameManager.TurretNopePrefab, position, rotation));
		placementObjects.Add(new Tuple<PlaceableTypes, bool>(PlaceableTypes.Fence, true), Instantiate(GameManager.WallPrefab, position, rotation));
		placementObjects.Add(new Tuple<PlaceableTypes, bool>(PlaceableTypes.Fence, false), Instantiate(GameManager.WallNopePrefab, position, rotation));
	}

	private void Update()
	{
		if (!GameManager.Interactable)
		{
			selectedPlacement = PlaceableTypes.None;
		}

		if (selectedPlacement == PlaceableTypes.None)
		{
			HideAllPlacementObjects();
			return;
		}

		GameObject placementObject = placementObjects[new Tuple<PlaceableTypes, bool>(selectedPlacement, true)];
		if (placementObject == null)
		{
			HideAllPlacementObjects();
			return;
		}

		BoxCollider placementObjectCollider = placementObject.GetComponent<BoxCollider>();
		if (placementObjectCollider == null)
		{
			throw new Exception("Placement Object has no Box Collider");
		}

		Vector3 mousePosition = Input.mousePosition;
		Vector3 direction = Camera.main.transform.forward;
		Vector3 position = Camera.main.ScreenToWorldPoint(mousePosition) + direction * 5f;
		Vector3 halfExtents = placementObjectCollider.size / 2f;
		Vector3 origin = position + halfExtents.y * placementObject.transform.up + direction * 5f;
		Quaternion rotation = Quaternion.Euler(Vector3.up * placementRotation);
		RaycastHit[] hits = Physics.BoxCastAll(origin, halfExtents, direction, rotation);
		placeableCenter = origin;
		placeableSize = halfExtents * 2f;
		bool placeable = hits.Length > 0;

		if (!placeable)
		{
			return;
		}

		Vector3 placementPosition = Vector3.zero;
		foreach (RaycastHit hit in hits)
		{
			switch (hit.transform.gameObject.layer)
			{
				case 8: //Units
				case 9: //Objects
					placeable = false;
					break;
				case 10: //Ground
					placementPosition = hit.point;
					break;
			}
		}

		placementObject = SetPlacementObject(selectedPlacement, placeable);

		if (placementObject == null)
		{
			return;
		}

		placementObject.transform.SetPositionAndRotation(position, rotation);

		if (placeable && Input.GetMouseButtonUp(0))
		{
			HarvestInfo harvestInfo = placementObject.GetComponent<HarvestInfo>();

			if (harvestInfo.GetCost() < GameManager.Harvest)
			{
				GameManager.Sow(harvestInfo.GetCost());
				GameObject newPlacement = Instantiate(placementObject, placementPosition, rotation);
			}
			else
			{
				GameManager.PromptManager.MoreGas();
			}
		}

		this.placeable = placeable;
	}

	private void FixedUpdate()
	{
		if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.W))
		{
			placementRotation++;
		}
		else if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.S))
		{
			placementRotation--;
		}

		while (placementRotation > 360)
		{
			placementRotation -= 360;
		}

		while (placementRotation < 0)
		{
			placementRotation += 360;
		}
	}

	private void HideAllPlacementObjects()
	{
		foreach (GameObject value in placementObjects.Values)
		{
			value.SetActive(false);
		}
	}

	private GameObject SetPlacementObject(PlaceableTypes type, bool placeable)
	{
		HideAllPlacementObjects();

		if (type == PlaceableTypes.None)
		{
			return null;
		}

		GameObject item = placementObjects[new Tuple<PlaceableTypes, bool>(type, placeable)];

		if (item != null)
		{
			item.SetActive(true);
		}

		return item;
	}

	public GameObject GetPlacementObject(PlaceableTypes type, bool placeable)
	{
		return placementObjects[new Tuple<PlaceableTypes, bool>(type, placeable)];
	}

	private void OnDrawGizmos()
	{
		Gizmos.color = placeable ? Color.green : Color.red;
		Gizmos.DrawWireCube(placeableCenter, placeableSize);
	}
}

public enum PlaceableTypes : short
{
	None,
	Harvester,
	Fence
};
