using UnityEngine;
using System.Collections;

public class WorldFrame : MonoBehaviour
{
	public const float FRAME_RADIUS = 15.0f;
	public const float FRAME_HEIGHT = 6.0f;
	public const float FRAME_THICKNESS = 0.25f;
	public const float FRAME_UV_U_MULT = 32.0f;
	public const float FRAME_UV_V_MULT = 4.0f;
	public const float FRAME_INNER_RADIUS = FRAME_RADIUS - 0.5f*FRAME_THICKNESS;
	public const float FRAME_OUTER_RADIUS = FRAME_RADIUS + 0.5f*FRAME_THICKNESS;
	const int FRAME_DETAIL_H = 256;
	const int FRAME_DETAIL_V = 5;
	const int FRAME_VERTS_PER_SLICE = 2*FRAME_DETAIL_V+4;
	const int FRAME_TRIS_PER_SLICE = 4*FRAME_DETAIL_V;

	GameObject NewBall (Vector3 pos, float radius, int index)
	{
		GameObject obj = new GameObject("Ball " + index);
		obj.transform.parent = transform;
		obj.transform.position = pos;

		obj.AddComponent<MeshFilter>();

		MeshRenderer mr = obj.AddComponent<MeshRenderer>();
		mr.castShadows = true;
		mr.receiveShadows = true;

		Rigidbody rb = obj.AddComponent<Rigidbody>();
		rb.useGravity = false;
		rb.interpolation = RigidbodyInterpolation.Interpolate;
		rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
		rb.mass = 64*radius*radius*radius;

		obj.AddComponent<SphereCollider>();

		Ball b = obj.AddComponent<Ball>();
		b.radius = radius;
		b.index = index;
		if (index == 0)
		{
			b.controllable = true;
			b.cameraFollows = true;
			b.lightFollows = true;
		}
		else
		{
			b.controllable = false;
			b.cameraFollows = false;
			b.lightFollows = false;
		}

		return obj;
	}

	GameObject[] problem_balls = new GameObject[8];
	GameObject[] balls = new GameObject[8];
	public GameObject cueball = null;
	int active_side = 1;
	GameObject field, field2;

	int GetNextBall()
	{
		for (int i=0; i<8; i++) if (balls[i] == null) return i+1;
		return -1;
	}

	bool Disaster()
	{
		for (int i=0; i<problem_balls.Length; i++)
		{
			if (problem_balls[i] == null)
			{
				problem_balls[i] = NewBall (new Vector3(0,active_side*6,0) ,0.35f,-1);
				return true;
			}
		}

		return false;
	}

	void Start ()
	{
		int num_verts = (FRAME_DETAIL_H+1)*FRAME_VERTS_PER_SLICE;
		int num_tris = FRAME_DETAIL_H*FRAME_TRIS_PER_SLICE;

		//Debug.Log("Frame: " + num_verts + " vertices, " + num_tris + " tris");

		Vector3[] p = new Vector3[num_verts];
		Vector3[] n = new Vector3[num_verts];
		Vector2[] uv = new Vector2[num_verts];
		int[] t = new int[3*num_tris];

		for (int i=0; i<=FRAME_DETAIL_H; i++)
		{
			int vs = i*FRAME_VERTS_PER_SLICE;
			int ts = i*FRAME_TRIS_PER_SLICE;

			float s = ((float)i)/FRAME_DETAIL_H;
			float cs = Mathf.Cos(2*Mathf.PI*s);
			float sn = Mathf.Sin(2*Mathf.PI*s);

			float u = FRAME_UV_U_MULT*s;

			p[vs+0].Set(FRAME_INNER_RADIUS*cs, -0.5f*FRAME_HEIGHT, FRAME_INNER_RADIUS*sn);
			p[vs+1].Set(FRAME_OUTER_RADIUS*cs, -0.5f*FRAME_HEIGHT, FRAME_OUTER_RADIUS*sn);
			p[vs+2].Set(FRAME_INNER_RADIUS*cs,  0.5f*FRAME_HEIGHT, FRAME_INNER_RADIUS*sn);
			p[vs+3].Set(FRAME_OUTER_RADIUS*cs,  0.5f*FRAME_HEIGHT, FRAME_OUTER_RADIUS*sn);

			n[vs+0].Set(0,-1,0);
			n[vs+1].Set(0,-1,0);
			n[vs+2].Set(0, 1,0);
			n[vs+3].Set(0, 1,0);

			uv[vs+0].Set(u,0);
			uv[vs+1].Set(u,FRAME_UV_V_MULT*(-FRAME_THICKNESS/(2*FRAME_HEIGHT + 2*FRAME_THICKNESS)));
			uv[vs+2].Set(u,FRAME_UV_V_MULT*(FRAME_HEIGHT/(2*FRAME_HEIGHT + 2*FRAME_THICKNESS)));
			uv[vs+3].Set(u,FRAME_UV_V_MULT*((FRAME_HEIGHT+FRAME_THICKNESS)/(2*FRAME_HEIGHT + 2*FRAME_THICKNESS)));

			for (int j=0; j<FRAME_DETAIL_V; j++)
			{
				float r = ((float)j)/(FRAME_DETAIL_V-1);
				p[vs+4               +j].Set(FRAME_INNER_RADIUS*cs, (r-0.5f)*FRAME_HEIGHT, FRAME_INNER_RADIUS*sn);
				p[vs+4+FRAME_DETAIL_V+j].Set(FRAME_OUTER_RADIUS*cs, (r-0.5f)*FRAME_HEIGHT, FRAME_OUTER_RADIUS*sn);

				n[vs+4               +j].Set(-cs,0,-sn);
				n[vs+4+FRAME_DETAIL_V+j].Set( cs,0, sn);

				uv[vs+4               +j].Set(u,FRAME_UV_V_MULT*(r*FRAME_HEIGHT/(2*FRAME_HEIGHT + 2*FRAME_THICKNESS)));
				uv[vs+4+FRAME_DETAIL_V+j].Set(u,FRAME_UV_V_MULT*((FRAME_THICKNESS+(2-r)*FRAME_HEIGHT)/(2*FRAME_HEIGHT + 2*FRAME_THICKNESS)));
			}

			if (i == FRAME_DETAIL_H) break;

			int next_vs = (i+1)*FRAME_VERTS_PER_SLICE;

			t[3*(ts+0)+0] = vs+0;
			t[3*(ts+0)+1] = vs+1;
			t[3*(ts+0)+2] = next_vs+0;

			t[3*(ts+1)+0] = next_vs+1;
			t[3*(ts+1)+1] = next_vs+0;
			t[3*(ts+1)+2] = vs+1;

			t[3*(ts+2)+0] = vs+3;
			t[3*(ts+2)+1] = vs+2;
			t[3*(ts+2)+2] = next_vs+3;
			
			t[3*(ts+3)+0] = next_vs+2;
			t[3*(ts+3)+1] = next_vs+3;
			t[3*(ts+3)+2] = vs+2;

			for (int j=0; j<FRAME_DETAIL_V-1; j++)
			{
				t[3*(ts+4+4*j+0)+0] = vs+4+j+1;
				t[3*(ts+4+4*j+0)+1] = vs+4+j;
				t[3*(ts+4+4*j+0)+2] = next_vs+4+j;

				t[3*(ts+4+4*j+1)+0] = next_vs+4+j;
				t[3*(ts+4+4*j+1)+1] = next_vs+4+j+1;
				t[3*(ts+4+4*j+1)+2] = vs+4+j+1;

				t[3*(ts+4+4*j+2)+0] = vs+4+FRAME_DETAIL_V+j;
				t[3*(ts+4+4*j+2)+1] = vs+4+FRAME_DETAIL_V+j+1;
				t[3*(ts+4+4*j+2)+2] = next_vs+4+FRAME_DETAIL_V+j;
				
				t[3*(ts+4+4*j+3)+0] = next_vs+4+FRAME_DETAIL_V+j+1;
				t[3*(ts+4+4*j+3)+1] = next_vs+4+FRAME_DETAIL_V+j;
				t[3*(ts+4+4*j+3)+2] = vs+4+FRAME_DETAIL_V+j+1;
			}
		}

		MeshFilter mf = gameObject.AddComponent<MeshFilter>();
		MeshRenderer mr = gameObject.AddComponent<MeshRenderer>();
		MeshCollider mc = gameObject.AddComponent<MeshCollider>();

		Mesh mesh = new Mesh();
		mesh.vertices = p;
		mesh.normals = n;
		mesh.uv = uv;
		mesh.triangles = t;
		mesh.Optimize();

		mf.sharedMesh = mesh;
		mc.sharedMesh = mesh;

		mc.material = Resources.Load<PhysicMaterial>("phys_materials/frame_physmat");

		Material mat = Resources.Load<Material>("materials/frame_mat");

		mr.sharedMaterial = mat;
		mr.castShadows = true;
		mr.receiveShadows = true;

		field = new GameObject("top_field");
		field.transform.parent = transform;
		field.AddComponent<WorldField>().index = 0;

		field2 = new GameObject("bottom_field");
		field2.transform.parent = transform;
		field2.transform.Rotate(new Vector3(180,0,0));
		field2.AddComponent<WorldField>().index = 1;

		cueball = NewBall (new Vector3(0,8,0), 0.25f, 0);
		balls[0] = NewBall (new Vector3(0,6,0), 0.25f, 1);
		/*for (int i=0; i<8; i++)
		{
			//balls[i] = NewBall (new Vector3((((i&1)==0)?1:-1)*0.5f*FRAME_RADIUS, (((i&4)==0)?1:-1)*8, (((i&2)==0)?1:-1)*0.5f*FRAME_RADIUS), 0.25f, i+1);
			float phi = 0.125f*i*2*Mathf.PI;
			balls[i] = NewBall (new Vector3(Mathf.Cos(phi)*0.25f*FRAME_RADIUS, 8, Mathf.Sin(phi)*0.25f*FRAME_RADIUS), 0.25f, i+1);
		}*/

		//SkyboxTemp();
	}

	/*void SkyboxTemp()
	{
		int sz = 1024;
		Color TopColor = new Color(81.0f/255.0f,145.0f/255.0f,231.0f/255.0f);
		Color BottomColor = new Color(216.0f/255.0f,96.0f/255.0f,130.0f/255.0f);

		Color[] img = new Color[sz*sz];

		float min_max_theta2 = Mathf.Asin(1/Mathf.Sqrt(3));
		min_max_theta2 *= min_max_theta2;

		for (int y=0; y<sz; y++)
		{
			float fy = 2*(float)y/(sz-1)-1;
			Color uc = (fy>0)?TopColor:BottomColor;
			for (int x=0; x<sz; x++)
			{
				float fx = 2*(float)x/(sz-1)-1;
				float r = Mathf.Sqrt(1 + fx*fx + fy*fy);
				float theta = Mathf.Asin(fy/r);
				float m = Mathf.Max(0,1-1.5f*theta*theta/min_max_theta2);
				float s = Mathf.Exp(-4*theta*theta)*m*m;
				img[y*sz + x] = s*Color.white + (1-s)*uc;
			}
		}

		Texture2D tex = new Texture2D(sz,sz);
		tex.SetPixels(img);
		tex.Apply();
		System.IO.File.WriteAllBytes(Application.dataPath + "/../tmp.png", tex.EncodeToPNG());
		Destroy (tex);
	}*/

	bool RegularBallsCorrect()
	{
		for (int i=0; i<8; i++)
		{
			if ((balls[i]!=null)&&(balls[i].transform.position.y*active_side > -1)) return false;
		}

		return true;
	}

	void Update ()
	{
		bool flipped = cueball.GetComponent<Ball>().hasFlipped;

		for (int i=0; i<8; i++)
		{
			if ((balls[i] != null) && balls[i].GetComponent<Ball>().hasFlipped)
			{
				if (balls[i].transform.position.y*active_side > 0)
				{
					balls[i].GetComponent<Ball>().Bad();
				}
				else
				{
					if (RegularBallsCorrect()) balls[i].GetComponent<Ball>().VeryGood();
					else balls[i].GetComponent<Ball>().Good();
				}
			}
		}

		if (cueball.transform.position.y*active_side < -0.5f)
		{
			bool rbc = RegularBallsCorrect();

			if (rbc)
			{
				for (int i=0; i<problem_balls.Length; i++)
				{
					if ((problem_balls[i] != null) && (problem_balls[i].transform.position.y*active_side>0))
					{
						Destroy (problem_balls[i]);
						problem_balls[i] = null;
					}
				}

				if (active_side > 0)
				{
					Destroy (field);
					
					field = new GameObject("top_field");
					field.transform.parent = transform;
					field.AddComponent<WorldField>().index = field2.GetComponent<WorldField>().index+1;
				}
				else
				{
					Destroy (field2);
					
					field2 = new GameObject("bottom_field");
					field2.transform.parent = transform;
					field2.transform.Rotate(new Vector3(180,0,0));
					field2.AddComponent<WorldField>().index = field.GetComponent<WorldField>().index+1;
				}
				
				int b = GetNextBall();
				if (b>0) balls[b-1] = NewBall(new Vector3(0,-active_side*6,0),0.25f,b);

				if ((b<=0) || (b>=7))
				{
					disaster_c++;
					for (int i=0; i<disaster_c; i++) Disaster();
				}
				
				active_side *= -1;
			}
			else if (flipped)
			{
				cueball.GetComponent<Ball>().Bad();
				Disaster();
			}
		}
	}

	int disaster_c = 0;
}
