using UnityEngine;
using System.Collections;


[System.Serializable]
/**
	This is a 2 dimenstional struct which unlike Vector2, only accepts int as its parameters. This is to provent the passing of fractional pixels.
	
	Note: These structs are handled in the background of the framework and you will likely never have to use them at high-level.
*/
public struct Point2D
{
	public int x;
	public int y;
	
	public Point2D(int _x, int _y)
	{
		x = _x;
		y = _y;
	}
}

[System.Serializable]
/**
	This is a 2 dimenstional struct which is used for describing the width and height of a quad.
	
	Note: These structs are handled in the background of the framework and you will likely never have to use them at high-level.
*/
public struct Dimension2D
{
	public float width;
	public float height;
	
	public Dimension2D(float w, float h)
	{
		width = w;
		height = h;
	}
}

[AddComponentMenu("Quad UI/Quad2D")]
[System.Serializable]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
/**
	This class is sealed an cannot be extended.
	
	This class is your communication to the Quad mesh. Though you can still communicate with the Renderer and Collider components, this class has functions for performing common operations.
	This class invokes RequireComponent on MeshFilter and MeshRenderer.
	
	Note: The MeshFilter, MeshRenderer and MeshCollider components are cached internally on Awake(), so communicating with them through this class is faster than using renderer or collider in any other attached script.
*/
sealed public class Quad2D : MonoBehaviour 
{
	bool _visible = true;
	MeshFilter _meshFilter;
	MeshRenderer _renderer;
	Collider _collider;
	Dimension2D _dimensions;
	
	void Awake()
	{
		CacheComponents();
		_dimensions = new Dimension2D(_meshFilter.mesh.bounds.size.x, _meshFilter.mesh.bounds.size.y);
	}
	
	public void CacheComponents()
	{
		_renderer = (MeshRenderer) GetComponent(typeof(MeshRenderer));
		_meshFilter = (MeshFilter) GetComponent(typeof(MeshFilter));
		_collider = (MeshCollider) GetComponent(typeof(MeshCollider));
	}
	
	/**
		Read only. Returns the cached Collider or null if there is none. Faster than using MonoBehaviour.collider in an attached script.
	*/
	new public Collider collider
	{
		get
		{
			return _collider;
		}
	}
	
	/**
		Read only. Returns the cached MeshRenderer. Faster than using MonoBehaviour.renderer in an attached script.
	*/
	new public MeshRenderer renderer
	{
		get
		{
			return _renderer as MeshRenderer;
		}
	}
	
	/**
		Enables/Disables attached MeshRenderer. Use this instead of manually toggling MeshRenderer.enabled.
	*/
	public bool visible
	{
		set
		{
			_visible = value;
			if(_renderer) _renderer.enabled = _visible;
		}
		get
		{
			_visible = _renderer.enabled;
			return _visible;
		}
	}
	
	/**
		Read only. Returns the width and height of the Quad as a Vector2.
	*/
	public Vector2 dimensions
	{
		get
		{
			return new Vector2(_dimensions.width,_dimensions.height);
		}
	}
	
	/**
		Takes a Point2D, the top-left coordinate of the UV map.
		
		Note: we use Point2D instead of Vector2 because Vectors support floats, but Point2D forces int. This prevents fractional pixels.
	*/
	public void UV(Point2D topLeftPixel)
	{
		MoveUV(topLeftPixel);
	}
	
	void MoveUV(Point2D newCoords)
	{
		Vector2[] newUV = new Vector2[4];
		
		//ADDED VERSION-SPECIFIC COMPILATION HERE --> 3.2 actually handles things as you'd expect.
		
		#if UNITY_3_2
		newUV[0] = CalcUV(new Point2D((int) newCoords.x, (int) newCoords.y)); //top left
		newUV[1] = CalcUV(new Point2D((int) newCoords.x + (int) _dimensions.width, (int) newCoords.y)); //top right
		newUV[2] = CalcUV(new Point2D((int) newCoords.x, (int) newCoords.y + (int) _dimensions.height)); //bottom left
		newUV[3] = CalcUV(new Point2D((int) newCoords.x + (int) _dimensions.width, (int) newCoords.y + (int) _dimensions.height)); //bottom right
		#else
		newUV[0] = CalcUV(new Point2D((int) newCoords.x + (int) _dimensions.width, (int) newCoords.y)); //top right
		newUV[1] = CalcUV(new Point2D((int) newCoords.x, (int) newCoords.y)); //top left
		newUV[2] = CalcUV(new Point2D((int) newCoords.x, (int) newCoords.y + (int) _dimensions.height)); //bottom left
		newUV[3] = CalcUV(new Point2D((int) newCoords.x + (int) _dimensions.width, (int) newCoords.y + (int) _dimensions.height)); //bottom right
		#endif
		//print(newUV[0] +","+newUV[1]+","+newUV[2]+","+newUV[3]);
		_meshFilter.mesh.uv = newUV;
	}
	
	Vector2 CalcUV(Point2D xy) //accepts the pixel coordinates of the 
	{
		Texture t = _renderer.sharedMaterial.GetTexture("_MainTex");
		Dimension2D wh = new Dimension2D(t.width,t.height);
		
		return new Vector2(xy.x / wh.width, (wh.height - xy.y) / wh.height);
	}
	
	Vector2 DecodeUV(Vector2 xy) //xy should be the normalized coordinates in UV space, this function then returns the UV coordinates in pixel coordinates
	{
		Texture t = _renderer.sharedMaterial.GetTexture("_MainTex");
		Dimension2D wh = new Dimension2D(t.width,t.height);
		
		return new Vector2(xy.x * wh.width, xy.y * wh.height);
	}
}
