using UnityEngine;
using System.Collections;

public class AtmosphereScattering : MonoBehaviour {
	
	const int details = 64;

	[SerializeField] public Gradient atmosphereColor;
	[SerializeField] public float radius = 6f;
	[SerializeField] public float visibleAngle = 20f;
	[SerializeField] public Transform targetCameraTransform = null;
	
	const int textureSize = 32;
	
	MeshFilter meshFilter;						
	MeshRenderer meshRenderer;
	Transform objectTransform;
//	float distanceToCamera = 0;
	
	void Awake () {
		meshFilter   = Utils.GetOrCreateComponent<MeshFilter>(gameObject);
		meshRenderer = Utils.GetOrCreateComponent<MeshRenderer>(gameObject);
		objectTransform = transform;
	}
	
	void Start () {
		meshFilter.mesh = CreateDiscMesh(radius);
		meshRenderer.sharedMaterial = CreateMaterial ();
		if (targetCameraTransform == null) {
			targetCameraTransform = Camera.allCameras[0].transform;
		}
	}

//	
//	void RefreshAtmosphericCone(float innerRadius, float outerRadius) {
//		if (innerRadius < distanceToCamera) {
//			float correctionAngle = Mathf.Acos(innerRadius / distanceToCamera);
//			float aspectZ = -Mathf.Cos(correctionAngle);
//			float aspectR = Mathf.Sin(correctionAngle);
//			for (int i = 0; i <= details; i ++) {
//				float angle = (360f / (details / (float)i)) / 180f * Mathf.PI;
//				normals [i * 2] = (new Vector3 (Mathf.Cos (angle) * aspectR, Mathf.Sin (angle) * aspectR, aspectZ)).normalized; 
//				normals [i * 2 + 1] = (new Vector3 (Mathf.Cos (angle) * aspectR, Mathf.Sin (angle) * aspectR, aspectZ)).normalized; 
//			}
//			for (int i = 0; i <= details; i++) {
//				vertexMeshComponent [i * 2] = normals [i * 2] * innerRadius;
//				vertexMeshComponent [i * 2 + 1] = normals [i * 2 + 1] * outerRadius;
//			}
//			meshFilter.mesh.normals = normals;
//			meshFilter.mesh.vertices = vertexMeshComponent;
//		}
//	}
	
	Mesh CreateDiscMesh(float innerRadius){
		int h = (details + 1);
		int w = 8;
		Vector3[] normals = new Vector3[h * w];
		Vector3[] vertexMeshComponent = new Vector3[h * w];
		Vector2[] textures = new Vector2[h * w];
		Color[] colorMeshComponent = new Color[h * w];
		int[] triangles    = new int[details * (w - 1) * 6];
		for(int j = 0; j < w; j ++){
			float zDist  = Mathf.Sin((float)j * 11f / 180f * Mathf.PI);
			float xyDist = Mathf.Cos((float)j * 11f / 180f * Mathf.PI);
			for(int i = 0; i <= details; i ++){
				float horAngle = (360f /(details / (float)i)) / 180f * Mathf.PI;
				normals[i * w + j] = (new Vector3( Mathf.Cos(horAngle) * xyDist, Mathf.Sin(horAngle) * xyDist, -zDist)).normalized;
			}
		}
		for (int j = 0; j < w; j ++) {
			for (int i = 0; i <= details; i++) {
				vertexMeshComponent [i * w + j] = normals [i * w + j] * innerRadius;
				colorMeshComponent [i * w + j] = new Color (1, 1, 1);
				textures [i * w + j] = new Vector2 ((float)i / (details), (float)(j) / (float)(w - 1));
			}
		}
		for (int j = 0; j < w - 1; j ++) {
			for (int i = 0; i < details; i++) {
				triangles [(i * (w - 1) + j) * 6 + 0] = (i) * w + (j);// 0 0
				triangles [(i * (w - 1) + j) * 6 + 1] = (i) * w + (j + 1);// 0 1
				triangles [(i * (w - 1) + j) * 6 + 2] = (i + 1) * w + (j);// 1 0
				triangles [(i * (w - 1) + j) * 6 + 3] = (i + 1) * w + (j);// 1 0
				triangles [(i * (w - 1) + j) * 6 + 4] = (i) * w + (j + 1);// 0 1
				triangles [(i * (w - 1) + j) * 6 + 5] = (i + 1) * w + (j + 1);// 1 1
			}
		}
		return new Mesh{ vertices = vertexMeshComponent, uv = textures, colors = colorMeshComponent, triangles = triangles, normals = normals };
	}
	
	Material CreateMaterial (){
		byte[] texData = new byte[textureSize * textureSize * 6];
		for (int i = 0; i < textureSize; i++) {
			for (int j = 0; j < textureSize; j++) {
				Color color = atmosphereColor.Evaluate((float)j / (float)(textureSize - 1));
				texData[(i + j * textureSize) * 4 + 0] = (byte)(color.r * 255f);
				texData[(i + j * textureSize) * 4 + 1] = (byte)(color.g * 255f);
				texData[(i + j * textureSize) * 4 + 2] = (byte)(color.b * 255f);
				texData[(i + j * textureSize) * 4 + 3] = (byte)(color.a * 255f);
			}
		}
		Texture2D texture = new Texture2D (textureSize, textureSize, TextureFormat.RGBA32, true);
		texture.LoadRawTextureData (texData);
		texture.Apply ();
		texture.filterMode = FilterMode.Trilinear;
		texture.wrapMode = TextureWrapMode.Clamp;
		Material material = new Material (Shader.Find ("Sprites/Diffuse"));
		material.mainTexture = texture;
		return material;
	}
	
	void Update () {
		if (targetCameraTransform != null) {
			objectTransform.rotation = targetCameraTransform.rotation;
		}
	}
}
