/**
 * Skript spravujici vkladani novych kosticek, jejich mazani a presun stavajicich. Ridi take zmenu jejich vlastnosti, jako material.
 */
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
/**
 * Author:			Karel Zavrel
 * 
 * Thesis:			An interactive application for development of creativity
 * Application:		Little Builder
 */
public class CreateObjectScript : MonoBehaviour {
	/**
	 * promenne:
	 * 	- nadrazeny prazdny objekt staveneho modelu
	 * 	- nove vlozena kosticka
	 * 	- obalovy objekt nove vkladane kosticky
	 * 	- panel s tlacitky pro pohyb nove vkladane kosticky
	 * 	- panel s tlacitky pro pohyb nove vkladane kosticky v Z ose
	 * 	- promenna pro urceni bodu na obrazovce
	 * 	- promenne slouzici k uchovani informace, zda bylo tlacitko "Dal" ci "Bliz" stisknuto
	 * 	- instance datove struktury Octree
	 * 	- implicitni material, ktereho nabyvaji vsechny nove vkladane kosticky
	 * 	- panel pro pohyb vybrane kosticky
	 * 	- panel pro zpravy zobrazovane uzivateli
	 * 	- textove pole pro zpravy uzivateli
	 */
	private GameObject rootBrick;
	private GameObject newBrick;
	private Bounds newBounds;
	private GameObject moveButtonPanel;
	private GameObject moveInZAxisPanel;
	private Vector3 screenPoint;
	private bool nearZButtonPressed = false, farZButtonPressed = false;
	private MyOctree myOctree = MyOctree.getInstance();
	private Material implicitMaterial;
	private GameObject moveSelectionPanel = null;
	private GameObject editorLogLabelPanel;
	private Text editorLogLabelText;

	void Awake(){
		editorLogLabelPanel = GameObject.Find ("EditorLogLabelPanel");
		editorLogLabelText = GameObject.Find ("EditorLogLabelText").GetComponent<Text> ();
	}
	/**
	 * funkce pouzita pro inicializaci promennych
	 */
	void Start () {
		/**
		 * nalezeni prazdneho objektu, ktery slouzi jako rodic vsem kostickam modelu - pro predstavu, reprezentuju GROUP
		 */
		rootBrick = GameObject.Find ("RootBrick");
		InnerStoringScript.setModelsRoot (rootBrick);
		InnerStoringScript.addRootBrickToListAsFirstElement(rootBrick);
		/**
		 * nastaveni implicitniho materialu
		 */
		implicitMaterial = Resources.Load ("Models/Materials/GreyColor") as Material;
		InnerStoringScript.setUsedMaterial (implicitMaterial);
		/**
		 * klasicky beh programu
		 */
		moveButtonPanel = GameObject.Find ("MoveBrickButtonPanel");
		moveButtonPanel.SetActive (false);
		moveInZAxisPanel = GameObject.Find ("TranslateZCoordinatePanel");
		moveInZAxisPanel.SetActive (false);
	}
	/**
	 * funkce volana kazdy frame
	 */
	void Update () {
		if (moveInZAxisPanel.activeSelf) {
			if (nearZButtonPressed) {
				moveNewBrickAtZCoordinates (-1);
			}
			if (farZButtonPressed) {
				moveNewBrickAtZCoordinates (1);
			}
		}
	}
	/**
	 * funkce pro vytvoreni nove kosticky
	 */
	public void createBrick(Button b){
		removeWrongInsertedBrickIfExists ();
		InnerStoringScript.setIsMinimapImageVisible (true);
		string[] splittedNameOfButton = b.name.Split (new char[]{ '_' }, 4);								// rozdeli jmeno tlacitka na 4 kusy dle _
		string brickName = splittedNameOfButton [0].Substring (0, splittedNameOfButton [0].Length - 1);		// z Bricks udela Brick
		string nameOfDirectory = splittedNameOfButton [0] + "_" + splittedNameOfButton[1];					// spoji Bricks , _ , 1 do Bricks_1
		brickName =  brickName + "_" + splittedNameOfButton[3] + "x" + splittedNameOfButton[1];				// Brick_1x1
		setCountsOfCleats(int.Parse(splittedNameOfButton[3]), int.Parse(splittedNameOfButton[1]));
		instantiateBrick (nameOfDirectory, brickName);
		newBounds = createNewBricksBounds (brickName.Substring (0, brickName.Length - 4), newBrick, float.Parse (splittedNameOfButton [3]), float.Parse (splittedNameOfButton [1]));
		InnerStoringScript.setNewlyInsertedBrick (newBrick);
		InnerStoringScript.setBrickToDeleted (newBrick);
		moveButtonPanel.SetActive (true);
	}
	/**
	 * funkce pro vytvoreni bounds pro nove vkladanou kosticku
	 */
	private Bounds createNewBricksBounds(string cleanBricksName, GameObject br, float xNum, float zNum){
		Bounds b;
		switch (cleanBricksName) {
			case "Brick":
			case "BevelledBrick":
			case "BevelledCornerBrick":
				b = new Bounds (br.transform.position, new Vector3 (xNum * 0.799f, 0.99f, zNum * 0.799f));
				break;
			case "LowerBrick":
				b = new Bounds (br.transform.position, new Vector3 (xNum * 0.799f, 0.32f, zNum * 0.799f));
				break;
			default:
				b = new Bounds ();
				break;
		}
		return b;
	}
	/**
	 * vytvori clone modelu nalezeneho na dane adrese, vlozi ho na pozici, natoci do zakladni polohy a za rodice mu da rootBrick
	 * 	-	pokud ma lichy pocet, tak upravi pocatecni Z-souranici na 0.4
	 */
	private void instantiateBrick(string nameOfDirectory, string brickName){
		if (InnerStoringScript.getOddCountOfCleatsZAxis ()) {
			newBrick = (GameObject)Instantiate (Resources.Load ("Models/" + nameOfDirectory + "/BrickPrefabs/" + brickName), 
					new Vector3 (25.0f, 0.0f, 0.4f), Quaternion.identity, rootBrick.transform);
		} else {
			if (brickName.Contains ("BevelledCornerBrick") && (brickName.Substring (brickName.Length - 3, 1) == "2")) {
				newBrick = (GameObject)Instantiate (Resources.Load ("Models/" + nameOfDirectory + "/BrickPrefabs/BevelledCornerBrick_2x2"), 
					new Vector3 (25.0f, 0.0f, 0.0f), Quaternion.identity, rootBrick.transform);
			} else if (brickName.Contains ("BevelledCornerBrick") && (brickName.Substring (brickName.Length - 3, 1) == "3")) {
				newBrick = (GameObject)Instantiate (Resources.Load ("Models/" + nameOfDirectory + "/BrickPrefabs/BevelledCornerBrick_3x3"), 
					new Vector3 (25.0f, 0.0f, 0.0f), Quaternion.identity, rootBrick.transform);
				InnerStoringScript.setOddCountOfCleatsXAxis (true);
				InnerStoringScript.setOddCountOfCleatsZAxis (true);
			} else if (brickName.Contains ("BevelledCornerBrick") && (brickName.Substring (brickName.Length - 3, 1) == "4")) {
				newBrick = (GameObject)Instantiate (Resources.Load ("Models/" + nameOfDirectory + "/BrickPrefabs/BevelledCornerBrick_4x4"), 
					new Vector3 (25.0f, 0.0f, 0.0f), Quaternion.identity, rootBrick.transform);
			} else {
				newBrick = (GameObject)Instantiate (Resources.Load ("Models/" + nameOfDirectory + "/BrickPrefabs/" + brickName), 
					new Vector3 (25.0f, 0.0f, 0.0f), Quaternion.identity, rootBrick.transform);
			}
		}
		newBrick.transform.localScale = new Vector3 (99.99f, 99.99f, 99.99f);
		if (InnerStoringScript.getUsedMaterial () != implicitMaterial) {
			changeBrickColor ();
		}
	}
	/**
	 * funkce pro zmenu materialu
	 * 	- musim menit material potomku, nebot obalovy objekt material nema
	 */
	private void changeBrickColor(){
		Transform[] allChildren = newBrick.GetComponentsInChildren<Transform>();
		for (int i = 1; i < allChildren.Length; i++) {
			allChildren [i].gameObject.GetComponent<MeshRenderer> ().material = InnerStoringScript.getUsedMaterial ();
		}
	}
	/**
	 * funkce pro nastaveni promennych, zda jsou pocty spuntiku liche
	 */
	private void setCountsOfCleats(int xAxis, int zAxis){
		if ((xAxis % 2) == 0) {
			InnerStoringScript.setOddCountOfCleatsXAxis (false);
		} else {
			InnerStoringScript.setOddCountOfCleatsXAxis (true);
		}
		if ((zAxis % 2) == 0) {
			InnerStoringScript.setOddCountOfCleatsZAxis (false);
		} else {
			InnerStoringScript.setOddCountOfCleatsZAxis (true);
		}
	}
	/**
	 * funkce volana pri stisku tlacitka moveButton
	 */
	public void setStartingScreenPoint(Button button){
		if (button.name == "MoveBrickButton") {
			newBrick = InnerStoringScript.getNewlyInsertedBrick ();
		} else if(button.name == "MoveSelectionButton"){
			InnerStoringScript.setMovePanelClicked (true);
			if (moveSelectionPanel == null) {
				moveSelectionPanel = GameObject.Find ("SelectionPanel");
			}
			newBrick = InnerStoringScript.getSelectedBrick ();
			newBounds = InnerStoringScript.getSelectedBrickBounds ();
			InnerStoringScript.removeBrickFromList (newBrick);
			InnerStoringScript.removeBoundsFromList (newBounds);
			InnerStoringScript.setBrickToDeleted (newBrick);
			if (!myOctree.removeBrickFromOctree (newBrick, newBounds)) {
				setLogLabel ("Chyba u vyndani kostky ze stromu.");
			}
			string[] partsOfBrickName = newBrick.name.Split (new char[]{ 'x' }, 2);
			string[] partsOfFirstPart = partsOfBrickName [0].Split (new char[]{ '_' }, 2);
			setCountsOfCleats (int.Parse (partsOfFirstPart [1]), int.Parse (partsOfBrickName [1].Substring (0, partsOfBrickName [1].IndexOf ('('))));
		}
		screenPoint = Camera.main.WorldToScreenPoint (newBrick.transform.position);
		InnerStoringScript.setIsRotationPermitted (false);
		InnerStoringScript.setIsScalePermitted (false);
		moveInZAxisPanel.SetActive (true);
	}
	/**
	 * funkce volona pri tazeni nove kosticky (po stisku tlacitka moveButton)
	 * 	-	pokud je lichy pocet spuntiku, tak v prislusne ose pridam 0.4 (pul kostky)
	 */
	public void moveNewBrick(){
		Vector3 curScreenPoint;
		if (Application.platform == RuntimePlatform.Android) {
			curScreenPoint = new Vector3(Input.touches[0].position.x, Input.touches[0].position.y, screenPoint.z);
		} else {
			curScreenPoint = new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenPoint.z);
		}
		Vector3 curPosition = Camera.main.ScreenToWorldPoint (curScreenPoint);
		if (InnerStoringScript.getOddCountOfCleatsXAxis ()) {
			newBrick.transform.position = new Vector3 (curPosition.x + 0.4f, curPosition.y, newBrick.transform.position.z);
		} else {
			newBrick.transform.position = new Vector3 (curPosition.x, curPosition.y, newBrick.transform.position.z);
		}
		newBounds.center = newBrick.transform.position;
		/**
		 * pro minimap
		 */
		InnerStoringScript.setNewlyInsertedBrick (newBrick);
	}
	/**
	 * funkce volana pri pusteni posunu nove kosticky
	 */
	public void deactivateMoveButtonPanel(){
		InnerStoringScript.setIsRotationPermitted (true);
		InnerStoringScript.setIsScalePermitted (true);
		if (moveButtonPanel.activeSelf) {
			moveButtonPanel.SetActive (false);
		}
		if ((InnerStoringScript.getSelectedBrick () != null) && (InnerStoringScript.getSelectedBrick ().name == newBrick.name)) {
			InnerStoringScript.setMoveOfSelectedBrickIsDone (true);
			InnerStoringScript.setMovePanelClicked (false);
			moveSelectionPanel.SetActive (false);
		}
		moveInZAxisPanel.SetActive (false);
		checkPositionOfNewBrick ();
		if (myOctree.intersectsWithRoot (newBounds) && myOctree.addBrickToOctree(newBrick, newBounds)) {
			InnerStoringScript.setNewlyInsertedBrick (newBrick);
			InnerStoringScript.addBoundsToList (newBounds);
			InnerStoringScript.addBrickToList (newBrick);
			InnerStoringScript.setSelectedBrick (null);
			InnerStoringScript.setSelectedBrickBounds (new Bounds ());
			setLogLabel ("Kostička byla správně přidána.");
		} else {
			removeWrongInsertedBrickIfExists ();
			setLogLabel ("Kostička nepřidána! Protínala se s jinou.");
		}
		InnerStoringScript.setIsMinimapImageVisible (false);
		newBrick = null;
	}
	/**
	 * funkce pro presun objektu v Z ose
	 */
	private void moveNewBrickAtZCoordinates(int direction){
		if (newBrick != null) {
			switch ((int) newBrick.transform.localEulerAngles.y) {
				case 90:
				case -270:
					newBrick.transform.Translate (Vector3.left * direction * 5.0f * Time.deltaTime);
					break;
				case 180:
				case -180:
					newBrick.transform.Translate (Vector3.back * direction * 5.0f * Time.deltaTime);
					break;
				case 270:
				case -90:
					newBrick.transform.Translate (Vector3.right * direction * 5.0f * Time.deltaTime);
					break;
				default:
					newBrick.transform.Translate (Vector3.forward * direction * 5.0f * Time.deltaTime);
					break;
			}
			newBounds.center = newBrick.transform.position;
		}
		/**
		 * pro minimap
		 */
		InnerStoringScript.setNewlyInsertedBrick (newBrick);
	}
	/**
	 * funkce pro nastaveni nearZButtonPressed na true
	 */
	public void pressNearButton(){
		if (!farZButtonPressed) {
			nearZButtonPressed = true;
		}
	}
	/**
	 * funkce pro nastaveni nearZButtonPressed na false
	 */
	public void releaseNearButton(){
		if (!farZButtonPressed) {
			nearZButtonPressed = false;
		}
	}
	/**
	 * funkce pro nastaveni farZButtonPressed na true
	 */
	public void pressFarButton(){
		if (!nearZButtonPressed) {
			farZButtonPressed = true;
		}
	}
	/**
	 * funkce pro nastaveni farZButtonPressed na false
	 */
	public void releaseFarButton(){
		if (!nearZButtonPressed) {
			farZButtonPressed = false;
		}
	}
	/**
	 * funkce pro smazani posledni radne pridane kosticky
	 */
	public void deleteLastBrick(){
		GameObject temporaryDeletedBrick = InnerStoringScript.removeLastBrick ();
		Bounds temporaryDeletedBounds = InnerStoringScript.removeLastBounds();
		if (temporaryDeletedBrick != null && temporaryDeletedBounds != null) {
			myOctree.removeBrickFromOctree(temporaryDeletedBrick, temporaryDeletedBounds);
			Destroy (temporaryDeletedBrick);
			setLogLabel ("Poslední přidaná kostička byla smazána.");
		}
	}
	/**
	 * funkce pro pripadne vynulovani nespravne vlozene kosticky
	 */
	private void removeWrongInsertedBrickIfExists(){
		GameObject temporaryBrick = InnerStoringScript.getBrickToBeDeleted ();
		if (temporaryBrick != null) {
			InnerStoringScript.setBrickToDeleted (null);
			Destroy (temporaryBrick);
			newBrick = null;
			newBounds = new Bounds ();
		}
	}
	/**
	 * funkce na upravu pozice nove vkladane kosticky, aby sedela v mrizce
	 * 	-	posun je o 0.8 v X a Z
	 * 	-	o 0.3333333 v Y
	 */
	private void checkPositionOfNewBrick(){
		float newX, newY, newZ;
		int xModNum = 8000000;
		int yModNum = 3333333;
		int zModNum = xModNum;
		if (InnerStoringScript.getOddCountOfCleatsXAxis ()) {
			if (InnerStoringScript.getOddCountOfCleatsZAxis ()) {
				newX = repairPosition (newBrick.transform.position.x - 0.4f, xModNum);
				newY = repairPosition (newBrick.transform.position.y, yModNum);
				newZ = repairPosition (newBrick.transform.position.z - 0.4f, zModNum);
				newBrick.transform.position = new Vector3(newX + 0.4f, newY, newZ + 0.4f);		
			} else {
				newX = repairPosition (newBrick.transform.position.x - 0.4f, xModNum);
				newY = repairPosition (newBrick.transform.position.y, yModNum);
				newZ = repairPosition (newBrick.transform.position.z, zModNum);
				newBrick.transform.position = new Vector3(newX + 0.4f, newY, newZ);
			}
		} else {
			if (InnerStoringScript.getOddCountOfCleatsZAxis ()) {
				newX = repairPosition (newBrick.transform.position.x, xModNum);
				newY = repairPosition (newBrick.transform.position.y, yModNum);
				newZ = repairPosition (newBrick.transform.position.z - 0.4f, zModNum);
				newBrick.transform.position = new Vector3(newX, newY, newZ + 0.4f);
			} else {
				newX = repairPosition (newBrick.transform.position.x, xModNum);
				newY = repairPosition (newBrick.transform.position.y, yModNum);
				newZ = repairPosition (newBrick.transform.position.z, zModNum);
				newBrick.transform.position = new Vector3(newX, newY, newZ);
			}
		}
		newBounds.center = newBrick.transform.position;
	}
	/**
	 * funkce, ktera upravuje pozici pres modulo - nutno takto, kvuli zapornym hodnotam
	 */
	private float repairPosition(float coordinate, int modNumber){
		bool condition = (coordinate >= 0);
		int coord = Mathf.Abs ((int)(coordinate * 10000000.0f));
		float result;
		if ((coord % modNumber) == 0) {
				result = (float)(coord / 10000000.0f);
		} else {
			if ((coord % modNumber) > (modNumber / 2)) {
				result = (float)((coord + modNumber - (coord % modNumber)) / 10000000.0f);
			} else {
				result = (float)((coord - (coord % modNumber)) / 10000000.0f);
			}
		}
		if (condition) {
			return result;
		} else {
			return result * (-1.0f);
		}
	}
	/**
	 * funkce pro otoceni kosticky doleva
	 */
	public void turnNewBrickToLeft(){
		if(newBrick.transform.localEulerAngles.y == -270){
			newBrick.transform.localEulerAngles = new Vector3 (0.0f, 0.0f, 0.0f);
		} else {
			newBrick.transform.localEulerAngles = new Vector3 (0.0f, newBrick.transform.localEulerAngles.y - 90.0f, 0.0f);
		}
		changeBoundsAndPositionWhenTurnBricks ();
	}
	/**
	 * funkce pro otoceni kosticky doprava
	 */
	public void turnNewBrickToRight(){
		if(newBrick.transform.localEulerAngles.y == 270){
			newBrick.transform.localEulerAngles = new Vector3 (0.0f, 0.0f, 0.0f);
		} else {
			newBrick.transform.localEulerAngles = new Vector3 (0.0f, newBrick.transform.localEulerAngles.y + 90.0f, 0.0f);
		}
		changeBoundsAndPositionWhenTurnBricks ();
	}
	/**
	 * funkce je pouzita pro upravu pozice pri otoceni kosticky
	 */
	private void changeBoundsAndPositionWhenTurnBricks(){
		newBounds = new Bounds (newBounds.center, new Vector3 (newBounds.size.z, newBounds.size.y, newBounds.size.x));
		if (InnerStoringScript.getOddCountOfCleatsXAxis () && !InnerStoringScript.getOddCountOfCleatsZAxis ()) {
			InnerStoringScript.setOddCountOfCleatsXAxis (false);
			InnerStoringScript.setOddCountOfCleatsZAxis (true);
		} else if (!InnerStoringScript.getOddCountOfCleatsXAxis () && InnerStoringScript.getOddCountOfCleatsZAxis ()) {
			InnerStoringScript.setOddCountOfCleatsXAxis (true);
			InnerStoringScript.setOddCountOfCleatsZAxis (false);
		}
		InnerStoringScript.setNewlyInsertedBrick (newBrick);
	}
	/**
	 * funkce pro nastaveni log label panelu
	 */
	private void setLogLabel(string t){
		editorLogLabelPanel.SetActive(true);
		editorLogLabelText.text = t;
		StartCoroutine (hideLogLabelPanel ());
	}
	/**
	 * funkce, ktera po 2 vterinach schova editorLogLabelPanel
	 */
	private IEnumerator hideLogLabelPanel(){
		yield return new WaitForSecondsRealtime (2);
		editorLogLabelText.text = "";
		editorLogLabelPanel.SetActive (false);
	}
}