﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Photon.Pun;
using UnityEngine.SceneManagement;
using PhotonHashTable = ExitGames.Client.Photon.Hashtable;

public class PlayerBase : MonoBehaviourPunCallbacks {

	// References
	CharacterController characterController;
	BasicCameraBehaviour basicCameraBehaviour;
    Wand activeWand;
	[HideInInspector]
	public Animator anim;
	GameObject drainObj, colObj;
	Vector3 spellScale = Vector3.one;
    RoundOverDisplay roundOverDisplay;
    DefeatedCounterDisplay defeatedCounterDisplay;
	Vector2[] selectSpots = new Vector2[] {new Vector2(-240, 60), new Vector2(-190, 60), new Vector2(-140, 60), new Vector2(-90, 60), new Vector2(-40, 60), new Vector2(-190, 110)};
	[HideInInspector]
	public PotionScript activePotion;

    // UI
    CanvasHandler canvasHandler;
	[HideInInspector]
	public ItemUI itemUI;
	[HideInInspector]
	public RectTransform selectBorder, healthBar, manaBar, energyBar;
	public Text healthText, manaText, energyText;
	[HideInInspector]
    public HitMarker hitMarker;
    [HideInInspector]
    public PauseAndQuit pauseAndQuit;

    // Movement Vals
    [HideInInspector]
	public float speed = 8f, sprintSpeed = 15f, jumpSpeed = 10f, camSpeed = 5f, gravity = 20f;
	float heightOffset = 1f, yRot, startSpeed;
	int jumpOffset = 0; // this variable is here because there is a weird bug with the character controller that for one frame after they land, they are set to not grounded, before being perminantly grounded
	[HideInInspector]
	public Vector3 playerRotation, pivotRotation;
	public float keySensitivity = 0.5f;

	// Meter Vals
	[HideInInspector]
	public float playerHealth, wandMana, manaRegenVal, buildEnergy, refreshRate;
	[HideInInspector]
	public Color energyColor;

	// Pivot Points
	[HideInInspector]
	public Transform camPivot;
	[HideInInspector]
	public GameObject neckPivot;

	// Boolean Vals
	[HideInInspector]
	public bool refreshed = true, inStorm = false, sprint = false, inAir = false, spawnDelay = true, defeated = false, jumped = false, drinking = false, delayMana = false, outOfBounds = false;

	// Velocities
	Vector3 forwardVelocity = Vector3.zero, horizontalVelocity = Vector3.zero, upwardVelocity = Vector3.zero, moveDirection = Vector3.zero;

	// Inventory
	[HideInInspector]
	public List<GameObject> inventory;
	[HideInInspector]
	public GameObject activeItem = null, healthPotion, builderWand;
	//hide for now
	[HideInInspector]
	public GameObject shieldPotion;
	[HideInInspector]
	public int activeItemNum = 0;
	[HideInInspector]
	public int lastItemNum = 0;

	// Magic Vals
	[HideInInspector]
	public GameObject spellPrefab;
	[HideInInspector]
	public GameObject orb, beam, scatter, burst, activeWandObj, activePotionObj;
	[HideInInspector]
	public Transform spawnPoint;
	[HideInInspector]
	public float spellTravelSpeed = 20f, drainSpeed = 0.5f, gainedEnergy = 0f;
	[HideInInspector]
	public bool autoFire, charging;
	[HideInInspector]
	public Transform magicSpawn, rightHand, leftHand;
	[HideInInspector]
	public LineRenderer energyBeam;
	[HideInInspector]
	public EnergyList buildEnergies;

	// Private Magic Vals
	Ray magicRay;
	RaycastHit hit;
	Vector3 magicDir;
	float gScale;
	bool canDrain, casting, draining = false, energyFull = false, buildMode = false;

    PlayerBase lastPlayerToDamagePlayer;
    bool spectating; // this is used to tell that this player is the one being spectating

    // Initialize Values
    void Awake() {
        lastPlayerToDamagePlayer = null;
        spectating = false;
        if (photonView && !photonView.IsMine && PhotonNetwork.IsConnected) {
            // get the last child of this object (the map marker) and disable it
            if(transform.childCount > 0) {
                transform.GetChild(transform.childCount - 1).gameObject.SetActive(false);
            }
            inventory.Clear();
            inventory.Add(builderWand);
            return;
        }
        playerHealth = 100f;
        wandMana = 0f;
        buildEnergy = 0f;
        startSpeed = speed;
        canDrain = false;
        energyBeam.SetPosition(0, leftHand.transform.position);
        energyBeam.SetPosition(1, leftHand.transform.position);
        anim = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
        inventory.Clear();
        buildEnergies.list.Clear();
        buildEnergies.list = new List<Energy>();
        if (!spellPrefab)
            spellPrefab = orb;
        while (inventory.Count < 6)
            inventory.Add(null);

		inventory [5] = builderWand;

        if (!camPivot) {
            camPivot = GameObject.FindGameObjectWithTag("Camera Pivot").transform;
			spawnPoint = camPivot;
            ItemDisplays.itemDisplay.playerBase = this;
        }
        if (!pauseAndQuit) {
            pauseAndQuit = GameObject.FindGameObjectWithTag("Pause Menu").GetComponent<PauseAndQuit>();
        }
        if (!selectBorder) {
            selectBorder = GameObject.FindGameObjectWithTag("Selection Border").GetComponent<RectTransform>();
        }
        if (!healthBar) {
            healthBar = GameObject.FindGameObjectWithTag("Health Bar").GetComponent<RectTransform>();
        }
        if (!manaBar) {
            manaBar = GameObject.FindGameObjectWithTag("Mana Bar").GetComponent<RectTransform>();
        }
		if (!energyBar) {
			energyBar = GameObject.FindGameObjectWithTag("Energy Bar").GetComponent<RectTransform>();
		}
		if (!healthText) {
			healthText = GameObject.FindGameObjectWithTag("Health Text").GetComponent<Text>();
		}
		if (!manaText) {
			manaText = GameObject.FindGameObjectWithTag("Mana Text").GetComponent<Text>();
		}
		if (!energyText) {
			energyText = GameObject.FindGameObjectWithTag("Energy Text").GetComponent<Text>();
		}
        if (!itemUI) {
            itemUI = GameObject.FindGameObjectWithTag("Item UI").GetComponent<ItemUI>();
        }
        if (!hitMarker) {
            hitMarker = GameObject.FindGameObjectWithTag("HitMarker").GetComponent<HitMarker>();
        }
        if (!basicCameraBehaviour) {
            //basicCameraBehaviour = camPivot.GetChild(0).GetComponent<BasicCameraBehaviour>();
        }
        if (!canvasHandler) {
            GameObject obj = GameObject.FindGameObjectWithTag("Game Manager");
            if (obj) {
                canvasHandler = obj.GetComponent<CanvasHandler>();
                if (!roundOverDisplay) {
                    roundOverDisplay = obj.GetComponent<RoundOverDisplay>();
                }
                if (!defeatedCounterDisplay) {
                    defeatedCounterDisplay = obj.GetComponent<DefeatedCounterDisplay>();
                }
            }
        }


        if (PhotonNetwork.IsConnected) {
            PhotonHashTable update = new PhotonHashTable() { { "remaining", true } };
            PhotonNetwork.LocalPlayer.SetCustomProperties(update);

        }
    }

    void FixedUpdate() {
        if (photonView && !photonView.IsMine && PhotonNetwork.IsConnected) {
            return;
        }
        HandleMana();
    }

	void Update () {
		AdjustedMouse ();
		if (photonView && !photonView.IsMine && PhotonNetwork.IsConnected) {
            if (spectating) {
                HandleCamPivot();
            }
            return;
        }
        CheckHealth ();
		CheckInput ();
		HandleCamPivot ();
		HandleEnergy ();
		AdjustBars ();

		selectBorder.anchoredPosition = selectSpots [activeItemNum];
		BuildWandRandomizer.isActive = builderWand.GetComponent<MeshRenderer> ().enabled;
	
		if (activeItem != null && !activeItem.GetComponent<PotionScript> ()) {
			anim.SetLayerWeight (1, 1f);
		} else {
			anim.SetLayerWeight (1, 0f);
		}
	}

    // trying to figure out how to get a rage quit / disconnect to drop their inventory
    //void OnDestroy() {
    //    if (SceneManager.GetActiveScene().name == "Launcher") {
    //        return;
    //    }
    //    if (photonView && photonView.Owner == null && !PhotonNetwork.IsMasterClient) {
    //        return;
    //    }

    //    if(!defeated) {
    //        DropAllItems();
    //    }
    //}

	void CheckHealth () {
		if (playerHealth > 100f)
			playerHealth = 100f;
		if (playerHealth <= 0f)
			Defeated ();
	}

	void CheckInput () {
        // Initial lock and unlock of curser, regardless of other inputs
        if (Input.GetMouseButtonDown(0) && !(roundOverDisplay && roundOverDisplay.IsRoundOver()) && !(pauseAndQuit && pauseAndQuit.paused)) {
            Cursor.visible = false;
            Cursor.lockState = CursorLockMode.Locked;
        }
        if (Input.GetKeyDown(KeyCode.Escape) || MapScript.toggleMap || AnimationWheel.toggleWheel) {
            Cursor.visible = true;
            Cursor.lockState = CursorLockMode.None;
        }

        if (defeated) {
            return;
        }

        // Check each input type if the emote wheel is not active
        if (!AnimationWheel.toggleWheel) {
			MovementInput ();
			if (!drinking) {
				MagicInput ();
				InventoryInput ();
			}
			PotionAnimation ();
		} else {
			ResetInputsAndAnims ();
		}

		// where to put you
		playerRotation.y = camPivot.eulerAngles.y;
		transform.eulerAngles = (AnimationWheel.toggleWheel || AnimationWheel.emoteActive) ? transform.eulerAngles : Vector3.Lerp (transform.eulerAngles, playerRotation, 1);
		Vector3 neckRot = new Vector3 (-transform.eulerAngles.y, 0f, 0f);
		neckPivot.transform.eulerAngles = transform.eulerAngles;

		// and you...
		energyBeam.SetPosition (0, leftHand.transform.position);
		if (!canDrain) {
			energyBeam.SetPosition (1, leftHand.transform.position);
		}

		magicRay = Camera.main.ViewportPointToRay (new Vector3 (0.5f, 0.5f, 0f));
        //Debug.LogError(magicRay.origin);
	}

	void ResetInputsAndAnims () {
		anim.SetFloat ("xDir", 0f);
		anim.SetFloat ("yDir", 0f);
		anim.SetLayerWeight (3, 0f);
		WandAnimsAndBools (false, false, false);
	}

	void MovementInput () {
		// Set speed and movement animation vals
		sprint = (Input.GetKey (KeyCode.LeftShift)) ? true : false;
		speed = (sprint) ? sprintSpeed : startSpeed;
		float animFloat = (sprint) ? 2f : 1f;
		anim.SetFloat ("xDir", Input.GetAxis ("Horizontal") * animFloat);
		anim.SetFloat ("yDir", Input.GetAxis ("Vertical") * animFloat);

		// Jump handling and animation triggers
		if (Physics.Raycast (transform.position, Vector3.down, characterController.bounds.extents.y)) {
			if (!characterController.isGrounded) {
				moveDirection.y -= gravity * Time.deltaTime;
			} else {
				if (inAir) {
					inAir = false;
					anim.SetTrigger ("Land");
					moveDirection.y = 0f;
					jumped = false;
					jumpOffset = 0;
				}
				if (Input.GetButton ("Jump")) {
					inAir = true;
					anim.SetTrigger ("Jump");
					jumped = true;
					moveDirection.y = jumpSpeed;
				}
			}
		} else {
			if (!jumped && jumpOffset > 1) {
				anim.SetTrigger ("Fall");
				inAir = true;
				jumped = true;
			}
			jumpOffset++;
			moveDirection.y -= gravity * Time.deltaTime;
		}

		// Set the movement velocities and move direction
		forwardVelocity = Input.GetAxis ("Vertical") * transform.forward * speed;
		horizontalVelocity = Input.GetAxis ("Horizontal") * transform.right * speed;
		upwardVelocity = transform.up * moveDirection.y;
		moveDirection = new Vector3(Input.GetAxis("Horizontal") * speed, moveDirection.y, Input.GetAxis("Vertical") * speed);

		// Animate and move the controller
		bool running = (Input.GetAxisRaw ("Horizontal") != 0f || Input.GetAxisRaw ("Vertical") != 0) ? true : false;
		characterController.Move((forwardVelocity + horizontalVelocity + upwardVelocity) * Time.deltaTime);
	}

	void MagicInput () {
		//Handle Resources
		EnergyDrain ();

		// Handle Magic Attacks
		if (activeWandObj) {
			activeWand = activeWandObj.GetComponent<WandScript> ().wand;
			SetActiveWandSpell (activeWand.type);
		
			if ((Input.GetButtonDown ("Fire1") || Input.GetKeyDown (KeyCode.H)) && wandMana > 0f && !canDrain) {
				if (activeWand.subType == WandSubType.Auto) {
					if (!autoFire)
						WandAnimsAndBools (true, false, true);
				} else if (activeWand.subType == WandSubType.Charged) {
					if (!charging)
						WandAnimsAndBools (false, true, true);
				} else {
					WandAnimsAndBools (false, false, true);
				}
			}
			if (Input.GetButtonUp ("Fire1")  || Input.GetKeyUp (KeyCode.H))
				WandAnimsAndBools (false, false, false);
		} else {
			activeWand = null;
		}
	}

	void PotionAnimation () {
		if (activePotionObj) {
			if ((Input.GetButtonDown ("Fire1") || Input.GetKeyDown (KeyCode.H)) && !drinking) {
				activePotion.stackNumber--;
				ItemDisplays.itemDisplay.UpdateAmount (activePotion);
				activePotionObj.SetActive (true);
				drinking = true;
				anim.SetLayerWeight (3, 1f);
				anim.SetTrigger ("Drink");
			}
		}
	}

	public void DrankPotion () {
		activePotionObj.SetActive (false);
		playerHealth += activePotion.potion.value;
		anim.SetLayerWeight (3, 0f);
		if (activePotion.stackNumber <= 0) {
			DropItem (activePotion.gameObject);
			if (PhotonNetwork.IsConnected && PhotonNetwork.IsMasterClient) {
				PhotonNetwork.Destroy (activePotion.gameObject);
			} else {
				Destroy (activePotion.gameObject);
			}
		}
		drinking = false;
	}

	public void UpdatePotionVals () {

	}


	void SetActiveWandSpell (WandType type){
        spellPrefab = GetSpellPrefab(type);
	}

    GameObject GetSpellPrefab(WandType type) {
        switch (type) {
            case WandType.Beam:
                return beam;
            case WandType.Scatter:
                return scatter;
            case WandType.Orb:
                return orb;
            case WandType.Burst:
                return burst;
            default:
                return null;
        }
    }

	void WandAnimsAndBools (bool auto, bool charge, bool trigger) {
		autoFire = auto;
		charging = charge;
		anim.SetBool ("Auto", auto);
		anim.SetBool ("Charged", charge);
		if(trigger)
			anim.SetTrigger ("LeftClick");
	}

	public void AnimCast () {
        if (photonView && !photonView.IsMine && PhotonNetwork.IsConnected)
            return;
        if (activeWand == null)
            return;
		if (!canDrain){
			if (activeWand.subType == WandSubType.None) {
				StartCoroutine (CastSpell (activeWand));
				if (!delayMana) {
					StartCoroutine (DelayMana ());
				}
			} else if (activeWand.subType == WandSubType.Auto)
				StartCoroutine (AutoCast (activeWand));
			else 
				StartCoroutine (ChargeCast (activeWand));
		}else{
			WandAnimsAndBools (false, false, false);
		}
	}

	IEnumerator DelayMana () {
		delayMana = true;
		yield return new WaitForSeconds (0.25f);
		delayMana = false;
		yield return null;
	}

	IEnumerator AutoCast (Wand wand) {
		StartCoroutine (CastSpell (wand));
		while (autoFire && wandMana > 0f) {
			yield return new WaitForSeconds (0.25f);
			StartCoroutine (CastSpell (wand));
		}
		yield return null;
	}

	IEnumerator ChargeCast (Wand wand) {
		float chargeTime = 0f;
		if (!charging) {
			StartCoroutine (CastSpell (wand));
		}
		spellScale = Vector3.one;
		wand.damage = wand.baseDamage;
		wand.spellSpeed = wand.baseSpeed;
		while (charging) {
			if (wandMana <= 0) {
				WandAnimsAndBools (false, false, false);
				break;
			}
			yield return new WaitForSeconds (0.01f);
			wandMana = wandMana - 0.1f;
			chargeTime++;
			if (chargeTime == 10) {
				if (wand.damage < wand.maxDamage) {
					wand.damage += 1f;
					if (wand.type == WandType.Orb) {
						spellScale += new Vector3 (0.1f, 0.1f, 0.1f);
					}
				}
				if (wand.type == WandType.Orb) {
					if (wand.spellSpeed > wand.minSpeed) {
						wand.spellSpeed -= 0.5f;
					} else {
						wand.spellSpeed = wand.minSpeed;
					}
				} else {
					if (wand.spellSpeed < wand.maxSpeed) {
						wand.spellSpeed += 0.5f;
					} else {
						wand.spellSpeed = wand.maxSpeed;
					}
				}
				chargeTime = 0;
			}
		}
		yield return null;
	}

	IEnumerator CastSpell (Wand wand) {
		magicDir = magicRay.direction;
		Quaternion rot = Quaternion.FromToRotation (spellPrefab.transform.forward, magicDir);
		Vector3 castPoint = spawnPoint.position;
        float[] xRotations = new float[wand.spellNum];
        float[] yRotations = new float[wand.spellNum];
        int[] projectileIDs = new int[wand.spellNum];
        for (int i = 0; i < wand.spellNum; i++) {
			if (wand.type == WandType.Scatter) {
                xRotations[i] = Random.Range(-0.05f, 0.05f);
                yRotations[i] = Random.Range(-0.05f, 0.05f);
                rot.x += xRotations[i];
                rot.y += yRotations[i];
            }
			GameObject spell = Instantiate (spellPrefab, castPoint, rot);
			if (wand.type == WandType.Orb) {
				spell.transform.localScale = spellScale;
			}
			spell.GetComponent<Rigidbody> ().velocity = spell.transform.forward * wand.spellSpeed;//magicDir

            Projectile projectileScript = spell.GetComponent<Projectile>();
			projectileScript.SetDamage (wand.damage);
            int projectileID = Projectile.GetNextAvailableProjectileID();
            projectileScript.SetProjectileID(projectileID);
			if(photonView && PhotonNetwork.IsConnected)
                projectileScript.SetOwnerViewID(photonView.ViewID);

            wandMana = wandMana - 1f;
			StartCoroutine (DestroyProjectile (spell));
			if (wand.type == WandType.Burst) {
				yield return new WaitForSeconds (0.05f);
			}
			if(photonView && PhotonNetwork.IsConnected && wand.type != WandType.Scatter && wand.type != WandType.Burst) {
                PhotonDebugger.IncrementNumberOfMessages("fire");
                photonView.RPC("FireProjectile", 
                    RpcTarget.Others, 
                    photonView.ViewID,
                    wand.type, 
                    spellPrefab.transform.forward, 
                    castPoint, 
                    magicDir,
                    spellScale,
					spell.transform.forward * wand.spellSpeed,
                    wand.damage,
                    projectileID
                );
            }
            projectileIDs[i] = projectileID;

        }
		if (photonView && PhotonNetwork.IsConnected) { 
            if (wand.type == WandType.Scatter) {
                PhotonDebugger.IncrementNumberOfMessages("fire");
                photonView.RPC("FireScatter",
                    RpcTarget.Others,
                    photonView.ViewID,
                    wand.type,
                    spellPrefab.transform.forward,
                    castPoint,
                    magicDir,
                    spellScale,
					wand.spellSpeed,
                    wand.damage,
                    wand.spellNum,
                    (object)xRotations,
                    (object)yRotations,
                    projectileIDs
                );
            }
            else if(wand.type == WandType.Burst) {
                PhotonDebugger.IncrementNumberOfMessages("fire");
                photonView.RPC("FireBurst",
                    RpcTarget.Others,
                    photonView.ViewID,
                    wand.type,
                    spellPrefab.transform.forward,
                    castPoint,
                    magicDir,
                    spellScale,
					wand.spellSpeed,
                    wand.damage,
                    wand.spellNum,
                    projectileIDs
                );
            }
        }
        wand.damage = wand.baseDamage;
		wand.spellSpeed = wand.baseSpeed;
		spellScale = Vector3.one;
	}

    [PunRPC]
    public void FireProjectile(int ownerViewID, int spellType, Vector3 fwd, Vector3 pos, Vector3 dir, Vector3 scl, Vector3 vel, float dmg, int projectileID) {
        WandType wandType = (WandType)spellType;
        GameObject spell = Instantiate(GetSpellPrefab(wandType), pos, Quaternion.FromToRotation(fwd, dir));
        if (wandType == WandType.Orb) {
            spell.transform.localScale = scl;
        }
        spell.GetComponent<Rigidbody>().velocity = vel;

        Projectile projectileScript = spell.GetComponent<Projectile>();
        projectileScript.SetDamage(dmg);
        projectileScript.SetProjectileID(projectileID);
        projectileScript.SetOwnerViewID(ownerViewID);

        StartCoroutine(DestroyProjectile(spell));
    }

    [PunRPC]
    public void FireScatter(int ownerViewID, int spellType, Vector3 fwd, Vector3 pos, Vector3 dir, Vector3 scl, float speed, float dmg, int projectileCount, float[] xRotations, float[] yRotations, int[] projectileIDs) {
        WandType wandType = (WandType)spellType;
        GameObject spellPrefab = GetSpellPrefab(wandType);
        Quaternion rot = Quaternion.FromToRotation(fwd, dir);
        for (int i = 0; i < projectileCount; i++) {
            if (wandType == WandType.Scatter) {
                rot.x += xRotations[i];
                rot.y += yRotations[i];
            }
            GameObject spell = Instantiate(spellPrefab, pos, rot);
            spell.GetComponent<Rigidbody>().velocity = spell.transform.forward * speed;

            Projectile projectileScript = spell.GetComponent<Projectile>();
            projectileScript.SetDamage(dmg);
            projectileScript.SetProjectileID(projectileIDs[i]);
            projectileScript.SetOwnerViewID(ownerViewID);

            StartCoroutine(DestroyProjectile(spell));
        }
    }

    [PunRPC]
    public void FireBurst(int ownerViewID, int spellType, Vector3 fwd, Vector3 pos, Vector3 dir, Vector3 scl, float speed, float dmg, int projectileCount, int[] projectileIDs) {
        StartCoroutine(FireRemoteBurst(ownerViewID, spellType, fwd, pos, dir, scl, speed, dmg, projectileCount, projectileIDs));
    }

    IEnumerator FireRemoteBurst(int ownerViewID, int spellType, Vector3 fwd, Vector3 pos, Vector3 dir, Vector3 scl, float speed, float dmg, int projectileCount, int[] projectileIDs) {
        WandType wandType = (WandType)spellType;
        GameObject spellPrefab = GetSpellPrefab(wandType);
        Quaternion rot = Quaternion.FromToRotation(fwd, dir);
        for (int i = 0; i < projectileCount; i++) {

            GameObject spell = Instantiate(spellPrefab, pos, rot);
            spell.GetComponent<Rigidbody>().velocity = spell.transform.forward * speed;

            Projectile projectileScript = spell.GetComponent<Projectile>();
            projectileScript.SetDamage(dmg);
            projectileScript.SetProjectileID(projectileIDs[i]);
            projectileScript.SetOwnerViewID(ownerViewID);

            StartCoroutine(DestroyProjectile(spell));

            yield return new WaitForSeconds(0.05f);
        }
    }

    void EnergyDrain () {
		if ((Input.GetButtonDown ("Fire2")  || Input.GetKeyDown (KeyCode.U)) && Physics.Raycast (magicRay, out hit)) {
			if (hit.collider != null) {
				if (CheckForDrainScript (hit.collider.gameObject) && hit.distance <= 40f && buildEnergy < 500) {
					colObj = hit.collider.gameObject;
					canDrain = true;
					anim.SetLayerWeight (2, 1f);
					anim.SetTrigger ("RightClick");
					anim.SetBool ("Drain", true);
				}
			}
		}

		if (Input.GetButton ("Fire2")  || Input.GetKey (KeyCode.U)) {
			if (canDrain) {
				if (Physics.Raycast (magicRay, out hit)) {
					if (hit.collider.gameObject == colObj && hit.distance <= 40f) {
						energyBeam.SetPosition (1, hit.point);
						draining = true;
					} else {
						StopDrain ();
						return;
					}
					if (!drainObj.GetComponent<MaterialDrainValue> ().DrainColor (this))
						StopDrain ();
				} else {
					StopDrain ();
				}
			}
		} else if (Input.GetButtonUp ("Fire2")  || Input.GetKeyUp (KeyCode.U)) {
			if (canDrain)
				anim.SetBool ("Drain", false);
		} else {
			if (draining) {
				StopDrain ();
				draining = false;
			}
		}
	}

	bool CheckForDrainScript (GameObject obj) {
		if (obj.GetComponent<MaterialDrainValue> ()) {
			if (obj.GetComponent<MaterialDrainValue> ().gScale < 1f) {
				drainObj = obj;
				return true;
			}
		} else if (obj.transform.parent) {
			return CheckForDrainScript (obj.transform.parent.gameObject);
		}

		drainObj = null;
		return false;
	}

	public void StopDrain () {
        if (photonView && PhotonNetwork.IsConnected && !photonView.IsMine)
            return;
		if (drainObj)
			drainObj.GetComponent<MaterialDrainValue> ().GetEnergy (this);

        canDrain = false;
		drainSpeed = 0f;
		drainObj = null;
		colObj = null;
		HandleEnergy ();
		StartCoroutine (DrainBeGone ());
	}


	IEnumerator DrainBeGone () {
		float t = 0;
		float layerWeight = 1;
		while (t < 1 && !canDrain) {
			t += Time.deltaTime * 3f;
			layerWeight = Mathf.Lerp (1, 0, t);
			anim.SetLayerWeight (2, layerWeight);
			yield return null;
		}
		yield return null;
	}

	void InventoryInput () {
		bool playerSwitchedItems = true;
		if ((Input.anyKeyDown || Input.GetAxisRaw ("Mouse ScrollWheel") != 0) && !Input.GetKeyDown (KeyCode.Q)) {
			AnimationWheel.emoteActive = false;
			if (buildMode) {
				if (Input.GetKeyDown (KeyCode.Alpha1)) {
					// Do Something
				} else if (Input.GetKeyDown (KeyCode.Alpha2)) {
					// Do Something
				} else if (Input.GetKeyDown (KeyCode.Alpha3)) {
					// Do Something
				} else if (Input.GetAxisRaw ("Mouse ScrollWheel") < 0) {
					// Do Something
				} else if (Input.GetAxisRaw ("Mouse ScrollWheel") > 0) {
					// Do Something
				}
				playerSwitchedItems = false;
			} else {
				if (Input.GetKeyDown (KeyCode.Alpha1)) {
					activeItemNum = 0;
				} else if (Input.GetKeyDown (KeyCode.Alpha2)) {
					activeItemNum = 1;
				} else if (Input.GetKeyDown (KeyCode.Alpha3)) {
					activeItemNum = 2;
				} else if (Input.GetKeyDown (KeyCode.Alpha4)) {
					activeItemNum = 3;
				} else if (Input.GetKeyDown (KeyCode.Alpha5)) {
					activeItemNum = 4;
				} else if (Input.GetAxisRaw ("Mouse ScrollWheel") < 0) {
					activeItemNum = (activeItemNum == 4) ? 0 : activeItemNum + 1;
				} else if (Input.GetAxisRaw ("Mouse ScrollWheel") > 0) {
					activeItemNum = (activeItemNum == 0) ? 4 : activeItemNum - 1;
				}

				if (lastItemNum == activeItemNum) {
					playerSwitchedItems = false;
				}
					
				lastItemNum = activeItemNum;
			}
		} else if (Input.GetKeyDown (KeyCode.Q)) {
			AnimationWheel.emoteActive = false;
			buildMode = !buildMode;
			activeItemNum = (activeItemNum == 5) ? lastItemNum : 5;
		} else {
            playerSwitchedItems = false;
        }

		if (playerSwitchedItems) {
			NewActiveItem (inventory [activeItemNum]);
			SetActiveItem ();
		}

		if (Input.GetKeyDown (KeyCode.F) && activeItem != null && activeItemNum != 5) {
            DropItem (activeItem);
        }
	}

	// This is the RPC that was used for switching items, but should change to save message rates
	void SetActiveItem () {
		if (photonView && PhotonNetwork.IsConnected) {
			if (inventory [activeItemNum] && inventory [activeItemNum].GetPhotonView ()) {
				PhotonDebugger.IncrementNumberOfMessages ("switch");
				photonView.RPC ("SwitchItem", RpcTarget.OthersBuffered, photonView.ViewID, inventory [activeItemNum].GetPhotonView ().ViewID);
			} else if (inventory [activeItemNum] == builderWand) {
				PhotonDebugger.IncrementNumberOfMessages ("switch");
				photonView.RPC ("SwitchItem", RpcTarget.OthersBuffered, photonView.ViewID, -2);
			} else if (inventory [activeItemNum] == null) {
				PhotonDebugger.IncrementNumberOfMessages ("switch");
				photonView.RPC ("SwitchItem", RpcTarget.OthersBuffered, photonView.ViewID, -1);
			}
		}

	}

    void NewActiveItem(GameObject item) {
		if (autoFire || charging) {
			anim.SetTrigger ("Cancel");
			autoFire = false;
			charging = false;
			spellScale = Vector3.one;
		}
        if (activeItem != null) {
			if(activeItem.tag != "Builder Wand")
            	activeItem.GetComponent<ItemScript>().isActive = false;
			if(activeItem.tag != "Potion" && activeItem.tag != "Builder Wand")
            	activeItem.SetActive(false);
			if (activeItem.tag == "Builder Wand")
				activeItem.GetComponent<MeshRenderer> ().enabled = false;
			if (item) {
				if (activeItem.tag == "Potion" && item.tag == "Wand") {
					activePotionObj = null;
				}
				if (activeItem.tag == "Wand" && item.tag == "Potion") {
					activeWandObj = null;
				}
			}
        }
        activeItem = item;
        if (activeItem != null) {
			if(activeItem.tag != "Potion" && activeItem.tag != "Builder Wand")
           		activeItem.SetActive(true);
			if(activeItem.tag != "Builder Wand")
            	activeItem.GetComponent<ItemScript>().isActive = true;
			if (activeItem.tag == "Builder Wand") {
				activeItem.GetComponent<MeshRenderer> ().enabled = true;
				activeWandObj = null;
				activePotionObj = null;
			}
        } else {
            activeWandObj = null;
			activePotionObj = null;
        }
    }

    void HandleMana () {
		if (refreshed) {
			if (wandMana >= 100f) {
				wandMana = 100f;
			} else if (wandMana < 0) {
				StartCoroutine (RefreshMana ());
			} else if(!charging && !autoFire && !delayMana) {
				wandMana = wandMana + manaRegenVal;
			}
		}
	}

	void HandleEnergy () {

		buildEnergy = buildEnergies.TotalEnergy ();

		if (buildEnergy >= 500f) {
			buildEnergy = 500f;
			energyFull = true;
		} else if (buildEnergy < 0f) {
			buildEnergy = 0f;
			energyFull = false;
		} else {
			energyFull = false;
		}
	}

	Vector2 AdjustedMouse () {
		float xAccess = 0f;
		float yAccess = 0f;

		if (Input.GetKey (KeyCode.I)) {
			xAccess -= 1 * keySensitivity;
		}
		if (Input.GetKey (KeyCode.K)) {
			xAccess += 1 * keySensitivity;
		}
		if (Input.GetKey (KeyCode.J)) {
			yAccess -= 1 * keySensitivity;
		}
		if (Input.GetKey (KeyCode.L)) {
			yAccess += 1 * keySensitivity;
		}

		xAccess -= Input.GetAxis ("Mouse Y");
		yAccess += Input.GetAxis ("Mouse X");

		return new Vector2 (xAccess, yAccess);
	}
		

	void HandleCamPivot () {
        if(!camPivot) {
            camPivot = GameObject.FindGameObjectWithTag("Camera Pivot").transform;
            return;
        }

		camPivot.position = new Vector3 (transform.position.x, transform.position.y + heightOffset, transform.position.z);
		pivotRotation += new Vector3 (AdjustedMouse ().x * camSpeed, AdjustedMouse ().y * camSpeed, 0f);

		if (pivotRotation.x > 35f) {
			pivotRotation.x = 35f;
		} else if (pivotRotation.x < -65f) {
			pivotRotation.x = -65f;
		}

		camPivot.eulerAngles = pivotRotation;
	}

	void AdjustBars () {
		if (healthBar) {
			healthBar.sizeDelta = new Vector2 (2.5f * playerHealth, healthBar.sizeDelta.y);
			string displayHealth = (playerHealth <= 0) ? "0" : Mathf.RoundToInt (playerHealth).ToString ();
			healthText.text = displayHealth + " / 100";
		}
		if (manaBar) {
			manaBar.sizeDelta = new Vector2 (2.5f * wandMana, manaBar.sizeDelta.y);
			string displayMana = (wandMana <= 0) ? "0" : Mathf.RoundToInt (wandMana).ToString ();
			manaText.text = displayMana + " / 100";
		}
		if (energyBar) {
			energyBar.sizeDelta = new Vector2 (0.29f * buildEnergy, energyBar.sizeDelta.y);
			string displayEnergy = (buildEnergy <= 0) ? "0" : Mathf.RoundToInt (buildEnergy).ToString ();
			energyText.text = displayEnergy + " / 500";
		}
	}

	IEnumerator RefreshMana () {
		wandMana = 0f;
		refreshed = false;
		yield return new WaitForSecondsRealtime (refreshRate);
		refreshed = true;
		yield return null;
	}

    void DropAllItems() {
        for(int i = 0; i < inventory.Count-1; i++) {
            if(inventory[i])
                DropItem(inventory[i], 500);
        }
    }

	public void DropItem (GameObject item, float forceMagnitude = 0, Vector3 direction = new Vector3()) {

		// check if the builder wand is the active item
		if (activeItem == builderWand) {
			buildMode = false;
			activeItemNum = lastItemNum;
			NewActiveItem (inventory [activeItemNum]);
			SetActiveItem ();
			item = activeItem;
			if (item == null)
				return;
		}

        // check if wand was the active one
        if (activeWandObj == item)
			activeWandObj = null;
		
		// disable the active potion
		activePotionObj = null;

        ItemScript itemScript = item.GetComponent<ItemScript>();

        // set bools
        itemScript.pickedUp = false;
        itemScript.isActive = false;
        itemScript.enabled = true;
        itemScript.EnableSphereCollider();

		// reset transform values
		item.transform.parent = null;
		if (item.gameObject.tag == "Potion") {
			item.transform.localScale = Vector3.one;
			item.transform.localEulerAngles = new Vector3 (-45f, 0f, 0f);
			foreach (GameObject part in item.GetComponent<PotionScript>().potionParts) {
				part.SetActive (true);
			}
		} else {
			item.transform.localScale = Vector3.one * 2.25f;
			item.transform.localEulerAngles = new Vector3 (-130f, 0f, 0f);
		}

        // disassociate the item from the player
        itemScript.myOwner = null;
		item.SetActive (true);
        int inventoryIndex = inventory.IndexOf(item);
        if (inventoryIndex >= 0)
            inventory[inventory.IndexOf(item)] = null;
        activeItem = null;

        WandScript wand = item.GetComponent<WandScript>();
        PotionScript potion = item.GetComponent<PotionScript>();
        if (wand) {
            wand.HideParticleEffects();
        }

        // anything that should happen to the local player but not remote players
		if (photonView && !photonView.IsMine && PhotonNetwork.IsConnected) {
            inventory.RemoveAll(GameObject => GameObject == null);
            return;
        }

        PhotonView itemPhotonView;
        if (itemPhotonView = item.GetComponent<PhotonView>()) {
            Vector3 dir = direction;
            if (forceMagnitude > 0) {
                if (dir == Vector3.zero) {
                    dir = Quaternion.Euler(0, Random.Range(0, 360), 0) * Vector3.forward;
                }
                if (wand)
                    wand.AddForceToItem(forceMagnitude, dir);
                if (potion)
                    potion.AddForceToItem(forceMagnitude, dir);
            } // the line is below
			if (PhotonNetwork.IsConnected && photonView.Owner != null) {
                if (!itemScript.stackable) {
                    PhotonDebugger.IncrementNumberOfMessages("drop item");
                    itemPhotonView.RPC("DropItem", RpcTarget.OthersBuffered, photonView.Owner.ActorNumber, itemPhotonView.ViewID, forceMagnitude, dir);
                }
                else {
                    PhotonDebugger.IncrementNumberOfMessages("drop item stack");
                    itemPhotonView.RPC("DropItemStack", RpcTarget.OthersBuffered, photonView.Owner.ActorNumber, itemPhotonView.ViewID, 0f, Vector3.zero, itemScript.stackNumber);
                }
                    
            }
        }

        ItemDisplays.itemDisplay.RemoveItem (activeItemNum);
	}

	void Defeated () {
		if(!defeated){

            if (PhotonNetwork.IsConnected) {
                PhotonHashTable update = new PhotonHashTable() { { "remaining", false } };
                PhotonNetwork.LocalPlayer.SetCustomProperties(update);
            }
            DropAllItems();
            anim.SetLayerWeight (1, 0f);
			anim.SetLayerWeight (2, 0f);
			anim.SetTrigger("Defeat");
			defeated = true;
            StartCoroutine(SwitchToSpectatorMode());
		}
	}

    IEnumerator SwitchToSpectatorMode() {
		if (!outOfBounds) {
			yield return new WaitForSeconds (3f);
		}

        if(roundOverDisplay && !roundOverDisplay.IsRoundOver()) {
            Transform t;
            SpectatorController spectatorController = roundOverDisplay.gameObject.GetComponent<SpectatorController>();
            if (!lastPlayerToDamagePlayer) {
                t = GetNewCameraTargetTransform();
            }
            else {
                t = lastPlayerToDamagePlayer.transform;
            }

            if (!t) {
                if (PhotonNetwork.IsConnected)
                    PhotonNetwork.Destroy(photonView);
                else
                    Destroy(gameObject);
                yield break;
            }

            spectatorController.SpectatePlayer(t);

            if (canvasHandler)
                canvasHandler.SetCanvasAsActive(CanvasHandler.CanvasType.Spectator);
            else
                Debug.Log("No Canvas Handler Set");
        }
        
        if (PhotonNetwork.IsConnected)
            PhotonNetwork.Destroy(photonView);
        else
            Destroy(gameObject);
    }

    public static Transform GetNewCameraTargetTransform() {
        if (!PhotonNetwork.IsConnected) {
            return null;
        }

        GameObject[] players = GameObject.FindGameObjectsWithTag("Player");
        for(int i = 0; i < players.Length; i++) {
            PhotonView view = players[i].GetPhotonView();
            if (view && !view.IsMine)
                return players[i].transform;
        }

        return null;
    }

    IEnumerator DestroyProjectile (GameObject magic) {
		yield return new WaitForSeconds (2f);
		Destroy (magic);
	}

    [PunRPC]
    public void SwitchItem(int playerID, int itemID) {
        if (photonView.ViewID != playerID) {
            return;
        }
		if (itemID == -1) {
			NewActiveItem (null);
			return;
		} else if (itemID == -2) {
			NewActiveItem (inventory [(photonView.IsMine ? inventory.Count - 1 : 0)]);
			return;
		}
        foreach(GameObject obj in inventory) {
            if (!obj)
                continue;
            if(obj.GetPhotonView() && obj.GetPhotonView().ViewID == itemID) {
                NewActiveItem(obj);
                return;
            }
        }
    }

    // returns if the damage defeated the player
    public bool DoDamage(float damage, PlayerBase damagingPlayer = null) {
        // do damage animations/sounds here

        if (SceneManager.GetActiveScene().name == "Launcher") {
            return false;
        }
        // if the round is over don't do any damage
        if (roundOverDisplay && roundOverDisplay.IsRoundOver()) {
            return false;
        }
        if (damagingPlayer) {
            lastPlayerToDamagePlayer = damagingPlayer;
        }
        if (playerHealth - damage > 0) {
            playerHealth -= damage;
            return false;
        }
        else {
            if (playerHealth > 0) {
                playerHealth = 0;
                return true;
            }
            else {
                playerHealth = 0;
                return false;
            }
        }
    }

    [PunRPC]
    public void ProjectileHit(int projectileID, int VictimID, int OwnerViewID, float damage) {
        // this will be called from the player who hit someone we need the local player
        if (!photonView.IsMine) {
            PlayerBase localPlayer = GetLocalPlayer();
            if(localPlayer)
                localPlayer.ProjectileHit(projectileID, VictimID, OwnerViewID, damage);
        }
        if (OwnerViewID == photonView.ViewID) {
            // handle hit marker effect
            StartCoroutine(hitMarker.Activate());
        }
        Projectile proj = Projectile.GetProjectile(projectileID);
        PlayerBase projectileOwner = null;
        GameObject[] allPlayers = GameObject.FindGameObjectsWithTag("Player");
        for (int i = 0; i < allPlayers.Length; i++) {
            if (allPlayers[i].GetPhotonView().ViewID == OwnerViewID) {
                projectileOwner = allPlayers[i].GetComponent<PlayerBase>();
            }
        }
        if (VictimID == photonView.ViewID && photonView.IsMine) {
            bool defeated = DoDamage(damage, projectileOwner);
            if(defeated) {
                photonView.RPC("PlayerDefeated", RpcTarget.OthersBuffered, VictimID, OwnerViewID);
            }
        }

        // Destroy the projectile with that ID
        if(proj && proj.gameObject)
            Destroy(proj.gameObject);
    }

    [PunRPC]
    public void PlayerDefeated(int VictimID, int OwnerViewID) {
        if(!photonView.IsMine) {
            PlayerBase localPlayer = GetLocalPlayer();
            if (localPlayer)
                localPlayer.PlayerDefeated(VictimID, OwnerViewID);
        }
        if (photonView.IsMine && OwnerViewID == photonView.ViewID) {
            defeatedCounterDisplay.SetDefeatedPlayersCount(defeatedCounterDisplay.GetDefeatedPlayersCount() + 1);
        }
    }

    public static PlayerBase GetLocalPlayer() {
        GameObject[] players = GameObject.FindGameObjectsWithTag("Player");
        for (int i = 0; i < players.Length; i++) {
            if (players[i].GetPhotonView().IsMine) {
                return players[i].GetComponent<PlayerBase>();
            }
        }
        return null;
    }

    public void SetSpectating(bool set) {
        spectating = set;
    }

    public bool IsSpectating() {
        return spectating;
    }

    public bool IsInBuildMode() {
        return buildMode;
    }
}
