using UnityEngine;
using System.Collections;

[RequireComponent (typeof (MeshFilter))]
[RequireComponent (typeof (MeshRenderer))]
public class PlanetarMesh : MonoBehaviour
{

	[SerializeField] public float radius = 6;
	[SerializeField] public Gradient groundColor = new Gradient();
	[SerializeField] public Gradient oceanColor = new Gradient();
	[SerializeField] public bool haveOcean = false;
	[SerializeField] public float oceanLevel = 0.2f;
	[SerializeField] public float oceanLevelShift = 0.02f;
	[SerializeField] public int textureSize = 512;
	[SerializeField] public int details = 18;
	[SerializeField] public bool isUnlit = false;
	[SerializeField] public bool isGasGiant = false; // TODO it - its fake

	protected MeshFilter meshFilter;						
	protected MeshRenderer meshRenderer;

	static Quaternion[] sides = new Quaternion[] {
		Quaternion.Euler(0,0,0),
		Quaternion.Euler(180,0,0),
		Quaternion.Euler(0, 90,0),
		Quaternion.Euler(0,-90,0),
		Quaternion.Euler( 90,0,0),
		Quaternion.Euler(-90,0,0)
	};

	Mesh CreateMesh(float radius){
		int f = details;
  		int h = (f + 1) * (f + 1);
		CombineInstance[] meshes = new CombineInstance[6];
		for (int z = 0; z < 6; z++) {
			Vector3[] normals  = new Vector3[h];
			Vector2[] textures = new Vector2[h];
			Vector3[] vertexMeshComponent = new Vector3[h];
			Color[] colorMeshComponent = new Color[h];
			int[] triangles    = new int[f * f * 2 * 6];
			for(int i = 0; i <= f; i ++){
				for(int j = 0; j <= f; j ++) {
					normals[i + j * (f + 1)] = (sides[z] * new Vector3( 1f - 2f * i / (f), 1f - 2f * j / (f), 1f)).normalized; 
				}
			}
			for(int i = 0; i <= f; i++) {
				for(int j = 0; j <= f; j++) {
					vertexMeshComponent[i + j * (f + 1)] = normals[i + j * (f + 1)] * radius;
					colorMeshComponent[i + j * (f + 1)] = new Color(1, 1, 1);
					textures[i + j * (f + 1)] = new Vector2(1 * (float)i / (f), 1 * (float)j / (f));
				}
			}
			for(int i = 0; i < f; i++) {
				for(int j = 0; j < f; j++) {
					triangles[(i + j * f) * 6 + 0] = i + j * (f + 1);
					triangles[(i + j * f) * 6 + 1] = i + 1 + (j) * (f + 1);
					triangles[(i + j * f) * 6 + 2] = i + (j + 1) * (f + 1);
					triangles[(i + j * f) * 6 + 3] = i + (j + 1) * (f + 1);
					triangles[(i + j * f) * 6 + 4] = i + 1 + (j) * (f + 1);
					triangles[(i + j * f) * 6 + 5] = i + 1 + (j + 1) * (f + 1);
				}
			}
			meshes[z] = new CombineInstance();
			meshes[z].transform = Matrix4x4.identity;
			meshes[z].mesh = new Mesh{ vertices = vertexMeshComponent, uv = textures, colors = colorMeshComponent, triangles = triangles, normals = normals };
		}

		Mesh combined = new Mesh ();
		combined.CombineMeshes (meshes, false);
		return combined;
	}

	void Awake () {
		meshFilter   = Utils.GetOrCreateComponent<MeshFilter>(gameObject);
		meshRenderer = Utils.GetOrCreateComponent<MeshRenderer>(gameObject);
	}
		
	void Start () {
		meshFilter.mesh = CreateMesh(radius);
		meshRenderer.sharedMaterials = CreateMaterial ();
	}
	
	void Update () {
	}

	Material[] CreateMaterial() {
		Material[] materials = new Material[6];
		float min = 1;
		float max = 0;
		for (int z = 0; z < 6; z ++) {
			byte[] colors = new byte[textureSize * textureSize * 4];
			for (int i = 0; i < textureSize; i++) {
				for (int j = 0; j < textureSize; j++) {
					Vector3 point = (sides[z] * new Vector3 (textureSize - i - textureSize * 0.5f, textureSize - j - textureSize * 0.5f, textureSize * 0.5f)).normalized;
					if (isGasGiant)
					{
						point.y *= 7f;
					}
//					float value = (Noise.Perlin (point, 2.1f) * 0.5f + Noise.Perlin (point, 4.8f) * 0.25f + Noise.Perlin (point, 8.7f) * 0.125f + Noise.Perlin (point, 16.05f) * 0.0525f + Noise.Perlin (point, 26.05f) * 0.0225f);
					float value = 0.5f + (Noise.Perlin (point, 2.1f) * 0.6f + Noise.Perlin (point, 4.8f) * 0.4f + Noise.Perlin (point, 8.7f) * 0.2f + Noise.Perlin (point, 16.07f) * 0.1f + Noise.Perlin (point, 28.07f) * 0.05f + Noise.Perlin (point, 50.07f) * 0.025f);// + Noise.Perlin (point, 101.07f) * 0.0125f);
					min = Mathf.Min(min, value);
					max = Mathf.Max(max, value);
					Color color;
					if (haveOcean) {
						if (value < oceanLevel) {
							color = oceanColor.Evaluate(value / oceanLevel);
						} else if (value > oceanLevel + oceanLevelShift)  {
							color = groundColor.Evaluate((value - oceanLevel - oceanLevelShift) / (1f - oceanLevel - oceanLevelShift));
						} else {
							float middle = (value - oceanLevel) / oceanLevelShift;
							color = Color.Lerp(oceanColor.Evaluate(1), groundColor.Evaluate(0), middle);
						}
					} else {
						color = groundColor.Evaluate(value);
					}
					colors [(i + j * textureSize) * 3 + 0] = (byte)(color.r * 255f);
					colors [(i + j * textureSize) * 3 + 1] = (byte)(color.g * 255f);
					colors [(i + j * textureSize) * 3 + 2] = (byte)(color.b * 255f);
				}
			}
			Texture2D tex = CreateTexture (colors, textureSize, textureSize);
			if (isUnlit)
			{
				materials [z] = new Material (Shader.Find ("Unlit/Texture"));
			}
			else
			{
				materials [z] = new Material (Shader.Find ("Mobile/Diffuse"));
			}
			materials [z].mainTexture = tex;
		}
		return materials;
	}

	static Texture2D CreateTexture(byte[] rawData, int width, int height) {
		Texture2D texture = new Texture2D (width, height, TextureFormat.RGB24, true);
		texture.LoadRawTextureData (rawData);
		texture.Apply ();
		texture.filterMode = FilterMode.Trilinear;
		texture.wrapMode = TextureWrapMode.Clamp;
		return texture;
	}

	#if UNITY_EDITOR		
	float currentRadius;
	
	void OnDrawGizmos() {
		bool needChange = false;		
		if (currentRadius != radius) {
			currentRadius = radius;
			needChange = true;
		}

		if (needChange){
			if (meshFilter == null) {
				meshFilter = gameObject.GetComponent<MeshFilter>();
			}
			meshFilter.mesh = CreateMesh(radius);
			Resources.UnloadUnusedAssets();
		}
	}	
	#endif
}
