using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

enum state {
	invalid=0,
	mainmenu=1,
	joinmenu=2,
	hostmenu=3,
	waitserver=4,
	waitclient=5,
	failconnect=6,
	playing=7
}


public class networkController : MonoBehaviour {
	public UILabel ipLabel; // inspector

	public static networkController controller;
	private int currentPoacherID = 0;
	private int currentTreasureID = 0;
	private int currentRampingRhinoID = 0;
	private int currentFoodID = 0;

	private string LocalAddress = "127.0.0.1";
	private string ServerAddress = "127.0.0.1";
	private int GameState = (int)state.waitserver;
	private int playerCount = 0;
	private bool LANOnly = true;
	private float nextNetworkUpdateTime = 0.0F;
	private List<Poacher> poachers = new List<Poacher>();
	private List<Treasure> treasures = new List<Treasure>();
	private List<Food> foods = new List<Food>();
	private List<RampagingRhino> rampagingRhinos = new List<RampagingRhino>();

	public Game game;
	private float networkUpdateIntervalMax = 0.05F;

	void Start () {
		controller = this;
		if (game.splitScreen) return;
		game.tempCamera.gameObject.SetActive (true);

		LocalAddress = GetLocalIPAddress();
		ServerAddress = LocalAddress;
		GameState = (int)state.mainmenu;
		Application.runInBackground = true;

		// which type?
		if (MenuController.hosting) {
			ipLabel.text = "IP: " + ServerAddress;
			StartServer();
			game.clientTreasureCountLabel.gameObject.SetActive (false);
			game.clientWaiting.gameObject.SetActive (false);
		}
		else {
			ipLabel.text = "";
			ServerAddress = MenuController.ip;
			ConnectToServer();
			game.healthLabel.gameObject.SetActive (false);
			game.treasureCountLabel.gameObject.SetActive (false);
			game.serverWaiting.gameObject.SetActive (false);
		}
	}

	void Update () {
		if(Network.isServer && Time.realtimeSinceStartup > nextNetworkUpdateTime)
		{
			nextNetworkUpdateTime = Time.realtimeSinceStartup + networkUpdateIntervalMax;
			if (game.GetWorld () && HasClients())
			{
				// Update Rhino
				networkView.RPC("UpdateRhino", RPCMode.Server,
					game.GetWorld().GetRhino().transform.position,
					game.GetWorld().GetRhino().rigidbody.velocity );

				// Update poachers
				List<Poacher> poacherQueue = new List<Poacher>();
				foreach(Poacher poacher in poachers) {
					if (poacher.NeedsUpdate()) {
						poacherQueue.Add(poacher);

						if (poacherQueue.Count == 10)
						{
							networkView.RPC("UpdatePoachers", RPCMode.Server,
								poacherQueue[0].GetPoacherID(), poacherQueue[0].transform.position, poacherQueue[0].transform.rotation,
								poacherQueue[1].GetPoacherID(), poacherQueue[1].transform.position, poacherQueue[1].transform.rotation,
								poacherQueue[2].GetPoacherID(), poacherQueue[2].transform.position, poacherQueue[2].transform.rotation,
								poacherQueue[3].GetPoacherID(), poacherQueue[3].transform.position, poacherQueue[3].transform.rotation,
								poacherQueue[4].GetPoacherID(), poacherQueue[4].transform.position, poacherQueue[4].transform.rotation,
								poacherQueue[5].GetPoacherID(), poacherQueue[5].transform.position, poacherQueue[5].transform.rotation,
								poacherQueue[6].GetPoacherID(), poacherQueue[6].transform.position, poacherQueue[6].transform.rotation,
								poacherQueue[7].GetPoacherID(), poacherQueue[7].transform.position, poacherQueue[7].transform.rotation,
								poacherQueue[8].GetPoacherID(), poacherQueue[8].transform.position, poacherQueue[8].transform.rotation,
								poacherQueue[9].GetPoacherID(), poacherQueue[9].transform.position, poacherQueue[9].transform.rotation);
							poacherQueue[0].UpdateLastSendPosition();
							poacherQueue[1].UpdateLastSendPosition();
							poacherQueue[2].UpdateLastSendPosition();
							poacherQueue[3].UpdateLastSendPosition();
							poacherQueue[4].UpdateLastSendPosition();
							poacherQueue[5].UpdateLastSendPosition();
							poacherQueue[6].UpdateLastSendPosition();
							poacherQueue[7].UpdateLastSendPosition();
							poacherQueue[8].UpdateLastSendPosition();
							poacherQueue[9].UpdateLastSendPosition();
							poacherQueue.Clear();
						}
					}
				}

				foreach(Poacher poacher in poacherQueue) {
					networkView.RPC("UpdatePoacher", RPCMode.Server, poacher.GetPoacherID(),
						poacher.transform.position, poacher.transform.rotation );
					poacher.UpdateLastSendPosition();
				}
				poacherQueue.Clear();

				// Update Rampaging Rhinos
				foreach(RampagingRhino rampagingRhino in rampagingRhinos) {
					if (rampagingRhino.NeedsUpdate()) {
						networkView.RPC("UpdateRampagingRhino", RPCMode.Server, rampagingRhino.GetRampagingRhinoID(),
							rampagingRhino.transform.position, rampagingRhino.transform.rotation );
						rampagingRhino.UpdateLastSendPosition();
					}
				}
			}
		}
	}
	
	public string GetLocalIPAddress()
	{
	   IPHostEntry host;
	   string localIP = "";
	   host = Dns.GetHostEntry(Dns.GetHostName());
	   foreach (IPAddress ip in host.AddressList)
	   {
	     if (ip.AddressFamily == AddressFamily.InterNetwork)
	     {
	       localIP = ip.ToString();
	     }
	   }
	   return localIP;
	}

	public int GetNextPoacherID() {
		return currentPoacherID++;
	}

	public int GetNextTreasureID() {
		return currentTreasureID++;
	}

	public int GetNextRampagingRhinoID() {
		return currentRampingRhinoID++;
	}

	public int GetNextFoodID() {
		return currentFoodID++;
	}
	
	void ConnectToServer()
	{
    	Network.Connect(ServerAddress, 25000);
		GameState = (int)state.waitserver;
	}
	
	void OnConnectedToServer()
	{
		game.tempCamera.gameObject.SetActive (false);
		game.clientWaiting.gameObject.SetActive (false);
		game.StartClient();
	}

	[RPC]
	void UpdateRhino(Vector3 position, Vector3 velocity)
	{
		//Debug.Log("Got rhino Update from server");
		if (game.GetClientWorld())
		{
			game.GetClientWorld().SetRhinoTarget(position);
			game.GetClientWorld().SetRhinoVelocity(velocity);
		}
	}

	public void SendSpawnPoacher(Poacher poacher) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendSpawnPoacher");
			poachers.Add (poacher);
			networkView.RPC ("SpawnPoacher", RPCMode.Server, poacher.GetPoacherID (), poacher.transform.position, poacher.transform.rotation);
		}
	}
	
	[RPC]
	void SpawnPoacher(int poacherID, Vector3 poacherPosition, Quaternion poacherRotation) {
		Debug.Log("SpawnPoacher");
		if (game.GetClientWorld ()) {
			game.GetClientWorld ().SpawnPoacher(poacherID, poacherPosition, poacherRotation);
		}
	}

	public void SendKillPoacher(Poacher poacher) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendKillPoacher");
			networkView.RPC ("KillPoacher", RPCMode.Server, poacher.GetPoacherID ());
		}
	}
	
	[RPC]
	void KillPoacher(int poacherID) {
		Debug.Log("KillPoacher");
		if (game.GetClientWorld ()) {
			game.GetClientWorld ().KillPoacher(poacherID);
		}
	}

	public void SendDestroyPoacher(Poacher poacher) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendDestroyPoacher");
			poachers.Remove (poacher);
			networkView.RPC ("DestroyPoacher", RPCMode.Server, poacher.GetPoacherID ());
		}
	}
	
	[RPC]
	void DestroyPoacher(int poacherID) {
		Debug.Log("DestroyPoacher");
		if (game.GetClientWorld ()) {
			game.GetClientWorld ().DestroyPoacher(poacherID);
		}
	}

	[RPC]
	void UpdatePoacher(int poacherID, Vector3 poacherPosition, Quaternion poacherRotation)
	{
		//Debug.Log("Got poacher update from server");
		if (game.GetClientWorld())
		{
			game.GetClientWorld().UpdatePoacher(poacherID, poacherPosition, poacherRotation);
		}
	}

	[RPC]
	void UpdatePoachers(
		int poacherID0, Vector3 poacherPosition0, Quaternion poacherRotation0,
		int poacherID1, Vector3 poacherPosition1, Quaternion poacherRotation1,
		int poacherID2, Vector3 poacherPosition2, Quaternion poacherRotation2,
		int poacherID3, Vector3 poacherPosition3, Quaternion poacherRotation3,
		int poacherID4, Vector3 poacherPosition4, Quaternion poacherRotation4,
		int poacherID5, Vector3 poacherPosition5, Quaternion poacherRotation5,
		int poacherID6, Vector3 poacherPosition6, Quaternion poacherRotation6,
		int poacherID7, Vector3 poacherPosition7, Quaternion poacherRotation7,
		int poacherID8, Vector3 poacherPosition8, Quaternion poacherRotation8,
		int poacherID9, Vector3 poacherPosition9, Quaternion poacherRotation9)
	{
		//Debug.Log("Got poachers updates from server");
		if (game.GetClientWorld())
		{
			game.GetClientWorld().UpdatePoacher(poacherID0, poacherPosition0, poacherRotation0);
			game.GetClientWorld().UpdatePoacher(poacherID1, poacherPosition1, poacherRotation1);
			game.GetClientWorld().UpdatePoacher(poacherID2, poacherPosition2, poacherRotation2);
			game.GetClientWorld().UpdatePoacher(poacherID3, poacherPosition3, poacherRotation3);
			game.GetClientWorld().UpdatePoacher(poacherID4, poacherPosition4, poacherRotation4);
			game.GetClientWorld().UpdatePoacher(poacherID5, poacherPosition5, poacherRotation5);
			game.GetClientWorld().UpdatePoacher(poacherID6, poacherPosition6, poacherRotation6);
			game.GetClientWorld().UpdatePoacher(poacherID7, poacherPosition7, poacherRotation7);
			game.GetClientWorld().UpdatePoacher(poacherID8, poacherPosition8, poacherRotation8);
			game.GetClientWorld().UpdatePoacher(poacherID9, poacherPosition9, poacherRotation9);
		}
	}

	public void SendSpawnTreasure(Treasure treasure) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendSpawnTreasure");
			treasures.Add (treasure);
			networkView.RPC ("SpawnTreasure", RPCMode.Server, treasure.GetTreasureID (), treasure.transform.position, treasure.transform.rotation);
		}
	}
	
	[RPC]
	void SpawnTreasure(int treasureID, Vector3 treasurePosition, Quaternion treasureRotation) {
		if (game.GetClientWorld ()) {
			Debug.Log("SpawnTreasure");
			game.GetClientWorld ().SpawnTreasure(treasureID, treasurePosition, treasureRotation);
		}
	}

	public void SendDestroyTreasure(Treasure treasure) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendDestroyTreasure");
			treasures.Remove (treasure);
			networkView.RPC ("DestroyTreasure", RPCMode.Server, treasure.GetTreasureID ());
		}
	}
	
	[RPC]
	void DestroyTreasure(int treasureID) {
		if (game.GetClientWorld ()) {
			Debug.Log ("DestroyTreasure");
			game.GetClientWorld ().DestroyTreasure(treasureID);
		}
	}

	public void SendSpawnFood(Food food) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendSpawnFood");
			foods.Add (food);
			networkView.RPC ("SpawnFood", RPCMode.Server, food.GetFoodID (), food.transform.position, food.transform.rotation);
		}
	}
	
	[RPC]
	void SpawnFood(int foodID, Vector3 foodPosition, Quaternion foodRotation) {
		if (game.GetClientWorld ()) {
			Debug.Log("SpawnFood");
			game.GetClientWorld ().SpawnFood(foodID, foodPosition, foodRotation);
		}
	}
	
	public void SendDestroyFood(Food food) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendDestroyFood");
			foods.Remove (food);
			networkView.RPC ("DestroyFood", RPCMode.Server, food.GetFoodID ());
		}
	}
	
	[RPC]
	void DestroyFood(int foodID) {
		if (game.GetClientWorld ()) {
			Debug.Log ("DestroyFood");
			game.GetClientWorld ().DestroyFood(foodID);
		}
	}

	public void SendSpawnRampagingRhino(RampagingRhino rampagingRhino, bool isMad) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendSpawnRampagingRhino");
			rampagingRhinos.Add (rampagingRhino);
			networkView.RPC ("SpawnRampagingRhino", RPCMode.Server, rampagingRhino.GetRampagingRhinoID (), rampagingRhino.transform.position, rampagingRhino.transform.rotation, isMad);
		}
	}
	
	[RPC]
	void SpawnRampagingRhino(int rampagingRhinoID, Vector3 rampagingRhinoPosition, Quaternion rampagingRhinoRotation, bool isMad) {
		Debug.Log("SpawnrampagingRhino");
		if (game.GetClientWorld ()) {
			game.GetClientWorld ().SpawnRampagingRhino(rampagingRhinoID, rampagingRhinoPosition, rampagingRhinoRotation, isMad);
		}
	}

	
	public void SendKillRampagingRhino(RampagingRhino rampagingRhino) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendKillRampagingRhino");
			networkView.RPC ("KillRampagingRhino", RPCMode.Server, rampagingRhino.GetRampagingRhinoID ());
		}
	}
	
	[RPC]
	void KillRampagingRhino(int rampagingRhinoID) {
		Debug.Log("KillRampagingRhino");
		if (game.GetClientWorld ()) {
			game.GetClientWorld ().KillRampagingRhino(rampagingRhinoID);
		}
	}
	
	public void SendDestroyRampagingRhino(RampagingRhino rampagingRhino) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("SendDestroyrampagingRhino");
			rampagingRhinos.Remove (rampagingRhino);
			networkView.RPC ("DestroyRampagingRhino", RPCMode.Server, rampagingRhino.GetRampagingRhinoID());
		}
	}
	
	[RPC]
	void DestroyRampagingRhino(int rampagingRhinoID) {
		Debug.Log("DestroyrampagingRhino");
		if (game.GetClientWorld ()) {
			game.GetClientWorld ().DestroyRampagingRhino(rampagingRhinoID);
		}
	}
	
	[RPC]
	void UpdateRampagingRhino(int rampagingRhinoID, Vector3 rampagingRhinoPosition, Quaternion rampagingRhinoRotation)
	{
		//Debug.Log("Got rampagingRhino update from server");
		if (game.GetClientWorld())
		{
			game.GetClientWorld().UpdateRampagingRhino(rampagingRhinoID, rampagingRhinoPosition, rampagingRhinoRotation);
		}
	}

	public void UpdateRhinoScore(int score) {
		if (Network.isServer && HasClients()) {
			Debug.Log ("UpdateRhinoScoreRPC");
			networkView.RPC ("UpdateRhinoScoreRPC", RPCMode.Server, score);
		}
	}
	
	[RPC]
	private void UpdateRhinoScoreRPC(int score)
	{

		Debug.Log("Got rhino score from server");
		if (game.GetClientWorld())
		{
			game.GetClientWorld().UpdateScore(score);
		}
	}

    void OnFailedToConnect(NetworkConnectionError error) 
	{
        Debug.Log("Could not connect to server: " + error);
		GameState = (int)state.failconnect;
    }

	void StartServer()
	{
		/*bool useNat=false;
		if (LANOnly==true)
			useNat=false;
		else
			useNat=!Network.HavePublicAddress();*/
		
		Network.InitializeServer(16,25000,false);
		
	}

	public bool HasClients() {
		return (playerCount > 0);
	}
	
	void OnServerInitialized()
	{
        Debug.Log("Server initialized and ready");
		GameState = (int)state.waitclient;
	}

	void OnPlayerConnected(NetworkPlayer p) 
	{
		if(Network.isServer)
		{
			if (!game.GetWorld()) {
				game.tempCamera.gameObject.SetActive (false);
				game.serverWaiting.gameObject.SetActive (false);
				game.StartServer(false);
			}

			playerCount++;
			Debug.Log("Player connected from " + p.ipAddress + ":" + p.port);
		
			// Send poachers
			foreach(Poacher poacher in poachers) {
				networkView.RPC("SpawnPoacher", RPCMode.Server, poacher.GetPoacherID(),
					poacher.transform.position, poacher.transform.rotation );
			}

			// Send treasures
			foreach(Treasure treasure in treasures) {
				networkView.RPC("SpawnTreasure", RPCMode.Server, treasure.GetTreasureID(),
					treasure.transform.position, treasure.transform.rotation );
			}

			// Send food
			foreach(Food food in foods) {
				networkView.RPC("SpawnFood", RPCMode.Server, food.GetFoodID(),
				 	food.transform.position, food.transform.rotation );
			}
		}
    }

	void OnPlayerDisconnected(NetworkPlayer player) 
	{
		playerCount--;
		Debug.Log("Player Disconnected");
    }

 	void OnGUI()
	{
		/*
		switch (GameState) 
		{
		case (int)state.mainmenu:
			if(GUILayout.Button("Join Game"))
			{
				GameState = (int)state.joinmenu;
			}
			if(GUILayout.Button("Host Game"))
			{
				GameState = (int)state.hostmenu;
			}
			if(GUILayout.Button("Quit"))
			{
				Application.Quit();
			}
			break;
		case (int)state.joinmenu:
			
			GUILayout.BeginHorizontal();
				GUILayout.Label("Server Address: ");
				ServerAddress = GUILayout.TextField(ServerAddress);
			GUILayout.EndHorizontal();
			
			LANOnly = GUILayout.Toggle(LANOnly, "Local Network Only");
			
			if(GUILayout.Button("Join!"))
			{
				ConnectToServer();
			}
			if(GUILayout.Button("Cancel"))
			{
				GameState = (int)state.mainmenu;
			}
			break;

		case (int)state.hostmenu:
			if(GUILayout.Button("Host!"))
			{
				StartServer();
			}
			if(GUILayout.Button("Cancel"))
			{
				GameState = (int)state.mainmenu;
			}
			break;

		case (int)state.waitserver:
			GUILayout.Label("Connecting...");
			if(GUILayout.Button("Cancel"))
			{
				Network.Disconnect();
				GameState = (int)state.joinmenu;
			}
			break;
			
		case (int)state.failconnect:
			GUILayout.Label("Connection to server failed");
			if(GUILayout.Button("I'll check my firewall, IP Address, Server Address, etc..."))
			{
				GameState = (int)state.joinmenu;
			}
			break;
			
		case (int)state.waitclient:
			GUILayout.Label("SERVER RUNNING");
			GUILayout.Label("waiting for connections...");
			GUILayout.Space(16);
			GUILayout.BeginHorizontal();
			if (playerCount > 0) {
				GUILayout.Label("No client connected");
			}
			else {
				GUILayout.Label("Client connected");
			}
			GUILayout.EndHorizontal();

			if(GUILayout.Button("Kill Server"))
			{
				Network.Disconnect();
				GameState = (int)state.hostmenu;
			}
			break;
			
		case (int)state.playing:
			GUILayout.Label("FPS Networking Sample");
			GUILayout.Label("---------------------");
			GUILayout.Label("WASD keys to move");
			GUILayout.Label("Hold down mouse button 1");
			GUILayout.Label("to mouselook, space to jump");
			break;	
		}
		*/
	}
	
}