using System;
using UnityEngine;
using Logger = Cryville.Common.Logger;

namespace Cryville.Crtr.Components {
	public class SpriteInfo {
		public string FrameName;
		public Cocos2dFrames.Frame Frame {
			get;
			private set;
		}
		public Rect Rect {
			get { return Frame.frame; }
		}
		/// <summary>
		/// The ratio of width divided by height.
		/// </summary>
		public float Ratio {
			get {
				return Rect.width / Rect.height;
			}
		}
		public void Load() {
			if (FrameName != null) {
				if (ChartPlayer.frames.ContainsKey(FrameName)) {
					Frame = ChartPlayer.frames[FrameName];
				}
				else {
					Logger.Log("main", 4, "Skin", "Texture {0} not found", FrameName);
				}
			}
		}
	}

	public class SpritePlane : SpriteBase {
		public SpritePlane() {
			SubmitProperty("frame", new PropOp.String(v => Frame = v));
			SubmitProperty("fit", new PropOp.Enum<FitMode>(v => Fit = v));
			SubmitProperty("opacity", new PropOp.Float(v => Opacity = v));
		}

		static Vector2[] _origuv;
		protected static Vector2[] OriginalUV {
			get {
				if (_origuv == null) {
					var m = GenericResources.Meshes["quad"];
					Vector2[] uv = new Vector2[m.vertices.Length];
					for (int i = 0; i < uv.Length; i++) {
						uv[i] = new Vector2(
							m.vertices[i].x == 0.5 ? 1 : 0,
							m.vertices[i].z == 0.5 ? 1 : 0
						);
					}
					_origuv = uv;
				}
				return _origuv;
			}
		}

		protected SpriteInfo frameInfo = new SpriteInfo();

		public string Frame {
			get { return frameInfo.FrameName; }
			set {
				frameInfo.FrameName = value;
				OnFrameUpdate();
			}
		}
		protected void OnFrameUpdate() {
			if (!mesh.Initialized) return;
			if (frameInfo.FrameName == null) {
				mesh.Renderer.enabled = false;
				return;
			}
			mesh.Renderer.enabled = true;
			frameInfo.Load();
			if (frameInfo.Frame != null)
				mesh.Renderer.material.mainTexture = frameInfo.Frame.Texture;
			else
				Logger.Log("main", 4, "Skin", "Unable to load texture {0}", frameInfo.FrameName);
			UpdateUV();
			UpdateScale();
			UpdateZIndex();
		}
		protected virtual void UpdateUV() {
			if (frameInfo.Frame == null) {
				Logger.Log("main", 4, "Skin", "Unable to load texture {0}", frameInfo.FrameName);
				return;
			}
			Vector2[] muv = OriginalUV;
			Vector2[] uv = new Vector2[muv.Length];
			for (int i = 0; i < uv.Length; i++) {
				uv[i] = frameInfo.Frame.GetUV(muv[i]);
			}
			mesh.Mesh.uv = uv;
		}

		float _opacity = 1;
		public float Opacity {
			get { return _opacity; }
			set {
				_opacity = value;
				UpdateOpacity();
			}
		}
		void UpdateOpacity() {
			if (!mesh.Initialized) return;
			var c = mesh.Renderer.material.color;
			c.a = _opacity;
			mesh.Renderer.material.color = c;
		}

		private FitMode m_fit = FitMode.height;
		public FitMode Fit {
			get { return m_fit; }
			set {
				m_fit = value;
				if (m_fit != FitMode.none && Scale.x != Scale.y) m_fit = FitMode.none;
			}
		}
		public enum FitMode {
			none, width, height
		}

		protected override void UpdateScale() {
			if (frameInfo.Frame == null) return;
			base.UpdateScale();
			if (m_fit != FitMode.none && Scale.x != Scale.y) m_fit = FitMode.none;
		}

		protected override Vector3 BaseScale {
			get {
				switch (m_fit) {
					case FitMode.none: return Vector3.one;
					case FitMode.width: return new Vector3(1, 1, 1 / frameInfo.Ratio);
					case FitMode.height: return new Vector3(frameInfo.Ratio, 1, 1);
					default: throw new NotSupportedException("Unsupported fit mode");
				}
			}
		}

		public override void Init() {
			frameInfo.Load();
			InternalInit();
			OnFrameUpdate();
			UpdateOpacity();
		}
	}
}
