using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Wrapper.Direct3D;
using Vortex.Wrapper;
using System.Drawing;
using Vortex.Debugging;

namespace Vortex.Drawing {
	/// <summary>
	/// Base texture class for common Texture and TargetTexture
	/// </summary>
	public abstract class BaseTexture : Resource, ISpriteSource {
		protected Direct3DTexture9 _texture;
		protected Surface _surface;
		private TextureOptions _options;

		private NamedTextureSpriteCollection _collection;

		protected Size _size;
		protected PixelFormat _format;
		private Vector2 _invSize;
		private Rect _region;

		#region Properties

		/// <summary>
		/// Gets the API object of texture.
		/// </summary>
		/// <value>The API object (Direct3DTexture9).</value>
		internal Direct3DTexture9 ApiRef {
			get { return _texture; }
		}

		/// <summary>
		/// Gets the format of texture.
		/// </summary>
		/// <value>The format of texture.</value>
		public PixelFormat Format {
			get { return _format; }
		}

		/// <summary>
		/// Gets the size of texture.
		/// </summary>
		/// <value>The size.</value>
		public Size Size {
			get { return _size; }
		}

		/// <summary>
		/// Gets the region of texture in pixels.
		/// </summary>
		/// <value>The region of texture.</value>
		public Rect Region {
			get { return _region; }
		}

		/// <summary>
		/// Gets the 1 / size of texture.
		/// </summary>
		/// <value>The 1 / size.</value>
		public Vector2 InvSize {
			get { return _invSize; }
		}

		/// <summary>
		/// Gets the named sprite collection associated with this texture.
		/// </summary>
		/// <value>The sprite collection.</value>
		public NamedTextureSpriteCollection Sprites {
			get { return _collection; }
		}

		/// <summary>
		/// Gets the texture options.
		/// </summary>
		/// <value>The texture options.</value>
		public TextureOptions Options {
			get { return _options; }
		}

		#endregion

		#region Protected Methods

		/// <summary>
		/// Initializes a new instance of the <see cref="BaseTexture"/> class.
		/// </summary>
		protected BaseTexture() {
			_options = new TextureOptions(this);
			_collection = new NamedTextureSpriteCollection(this);
		}

		/// <summary>
		/// Validates the size of texture.
		/// </summary>
		/// <param name="width">The width.</param>
		/// <param name="height">The height.</param>
		protected void ValidateSize(int width, int height) {
			if (width < 1) throw new ArgumentException("Texture width should be greater or equal 1", "width");
			if (width < 1) throw new ArgumentException("Texture height should be greater or equal 1", "height");
		}

		/// <summary>
		/// Initializes texture instance the with API object (Direct3DTexture9).
		/// </summary>
		/// <param name="texture">The API texture object.</param>
		protected void Initialize(Direct3DTexture9 texture) {
			_texture = texture;
			Direct3DSurface9 refSurface;
			HResult result = _texture.GetSurfaceLevel(0, out refSurface);
			Watcher.AssertCritical(result, "Get texture surface 0 level");

			_surface = new Surface(Device, refSurface);
			_size = _surface.Size;
			_format = _surface.Format;
			_invSize = Vector2.Unit / new Vector2(_surface.Size);
			_region = new Rect(Point.Empty, _surface.Size);
		}

		/// <summary>
		/// Invoked before the device is reset to free top surface reference.
		/// </summary>
		protected override void BeforeDeviceReset() {
			_surface.Free();
		}

		/// <summary>
		/// Invoked after the device is reset successfully.
		/// </summary>
		protected override void AfterDeviceReset() {
			Initialize(_texture);
		}

		/// <summary>
		/// Frees the internal data of texture.
		/// </summary>
		protected override void FreeData() {
			//free surface
			if (null != _surface) {
				_surface.Free();
				_surface = null;
			}

			//free texture
			if (null != _texture) {
				_texture.Dispose();
				_texture = null;
			}			
		}

		#endregion

		#region Coordinate Transforms

		/// <summary>
		/// Transforms the specified rect into texture coord-space.
		/// </summary>
		/// <param name="rect">The rect.</param>
		/// <param name="result">The result of transformation.</param>
		public void Transform(ref Rect rect, out Rect result) {
 			result.Left = rect.Left * _invSize.X;
			result.Top = rect.Top * _invSize.Y;
			result.Right = rect.Right * _invSize.X;
			result.Bottom = rect.Bottom * _invSize.Y;
		}

		/// <summary>
		/// Transforms the specified vector into texture coord-space.
		/// </summary>
		/// <param name="vector">The vector.</param>
		/// <param name="result">The result of transformation.</param>
		public void Transform(ref Vector2 vector, out Vector2 result) {
 			result.X = vector.X * _invSize.X;
			result.Y = vector.Y * _invSize.Y;
		}

		#endregion


		#region ISpriteProvider Members

		/// <summary>
		/// Returns the sprite which describes entire texture.
		/// </summary>
		/// <returns><see cref="Sprite"/> instance.</returns>
		public Sprite ToSprite() {
			return new Sprite(this);
		}

		#endregion

		/// <summary>
		/// Returns the sprite which describes specified region of this texture.
		/// </summary>
		/// <returns><see cref="Sprite"/> instance.</returns>
		public Sprite ToSprite(Rect region) {
			return new Sprite(this, region);
		}

		/// <summary>
		/// Returns the sprite which describes specified region of this texture.
		/// </summary>
		/// <param name="x">The x coord of left top sprite position.</param>
		/// <param name="y">The y coord of left top sprite position.</param>
		/// <param name="width">The width of sprite.</param>
		/// <param name="height">The height of sprite.</param>
		/// <returns><see cref="Sprite"/> instance.</returns>
		public Sprite ToSprite(float x, float y, float width, float height) {
			return new Sprite(this, x, y, width, height);
		}

		/// <summary>
		/// Generates sprite grid from entire texture.
		/// </summary>
		/// <param name="gridWidth">Width of the sprite grid.</param>
		/// <param name="gridHeight">Height of the sprite grid.</param>
		/// <returns>Result sprite grid</returns>
		public SpriteGrid ToGrid(int gridWidth, int gridHeight) {
			return ToSprite().Split(gridWidth, gridHeight);
		}

		#region Support Of Beauty of Inteface

		/// <summary>
		/// Implements the operator *. It allows to use simple notation of setting texture to specified sampler,
		/// e.g using(canvas &lt;= texture * 3) { ... }
		/// </summary>
		/// <param name="texture">The texture to set.</param>
		/// <param name="index">The index of sampler.</param>
		/// <returns>The result of the operator - texture sampler.</returns>
		public static TextureIndex operator * (BaseTexture texture, int index) {
			return new TextureIndex(index, texture);
		}

		#endregion
	}
}
