using Cryville.Common;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using UnityEngine;

namespace Cryville.Crtr {
	[BinderAttribute(typeof(Cocos2dFramesBinder))]
	public class Cocos2dFrames {
		public Metadata metadata;
		public class Metadata {
			public int format;
			public Vector2 size;
			public string textureFileName;
		}

		public Dictionary<string, Frame> frames;
		public class Frame {
#pragma warning disable IDE1006
			Rect _frame;
			public Rect frame {
				get { return _frame; }
				set { _frame = value; }
			}
			public Rect textureRect {
				get { return _frame; }
				set { _frame = value; }
			}

			bool _rotated = false;
			public bool rotated {
				get { return _rotated; }
				set { _rotated = value; }
			}
			public bool textureRotated {
				get { return _rotated; }
				set { _rotated = value; }
			}
#pragma warning restore IDE1006

			public Vector2 offset;

			public Rect sourceColorRect;
			public Vector2 sourceSize;
			private Rect _uv;
			private Vector2[] cuv;
			public Rect UV {
				get {
					return _uv;
				}
				private set {
					_uv = value;
					float x0 = Mathf.Min(_uv.xMin, _uv.xMax);
					float x1 = Mathf.Max(_uv.xMin, _uv.xMax);
					float y0 = Mathf.Min(_uv.yMin, _uv.yMax);
					float y1 = Mathf.Max(_uv.yMin, _uv.yMax);
					if (_rotated) cuv = new Vector2[]{
						new Vector2(x0, y1),
						new Vector2(x1, y0),
						new Vector2(x0, y0),
						new Vector2(x1, y1),
					};
					else cuv = new Vector2[]{
						new Vector2(x0, y0),
						new Vector2(x1, y1),
						new Vector2(x1, y0),
						new Vector2(x0, y1),
					};
				}
			}
			public Vector2 GetUV(Vector2 uv) {
				return GetUV(uv.x, uv.y);
			}
			public Vector2 GetUV(float u, float v) {
				Vector2 uv00 = cuv[0], uv11 = cuv[1],
				uv10 = cuv[2], uv01 = cuv[3];
				return (1 - u - v) * uv00
					+ u * uv10
					+ v * uv01
					+ u * v * (uv00 + uv11 - uv10 - uv01);
			}
			public Texture2D Texture {
				get;
				private set;
			}
			public Vector2 Size {
				get {
					return new Vector2(Texture.width, Texture.height);
				}
			}
			
			public void Init() {
				if (Texture == null)
					throw new InvalidOperationException(); // TODO
				_frame = new Rect(Vector2.zero, Size);
				var w = _frame.width;
				var h = _frame.height;
				float x = _frame.x / w;
				float y = 1 - _frame.y / h;
				float tw = (_rotated ? _frame.height : _frame.width) / w;
				float th = (_rotated ? _frame.width : _frame.height) / h;
				if (_rotated) UV = new Rect(x, y, tw, -th);
				else UV = new Rect(x, y, tw, -th);
			}

			public void Init(int w, int h, Texture2D _base) {
				if (Texture != null)
					throw new InvalidOperationException(); // TODO
				Texture = _base;
				float x = _frame.x / w;
				float y = 1 - _frame.y / h;
				float tw = (_rotated ? _frame.height : _frame.width) / w;
				float th = (_rotated ? _frame.width : _frame.height) / h;
				if (_rotated) UV = new Rect(x, y, tw, -th);
				else UV = new Rect(x, y, tw, -th);
			}
			public Frame() { }
			public Frame(Texture2D tex) {
				Texture = tex;
			}
		}

		Texture2D _base;

		public void Init(Dictionary<string, Texture2D> texs) {
			_base = texs[StringUtils.TrimExt(metadata.textureFileName)];
			var w = (int)metadata.size.x;
			var h = (int)metadata.size.y;
			if (w == 0 || h == 0) {
				w = _base.width;
				h = _base.height;
			}
			foreach (var f in frames) {
				f.Value.Init(w, h, _base);
			}
		}
	}

	public class Cocos2dFramesBinder : EmptyBinder {
		public override object ChangeType(object value, Type type, CultureInfo culture) {
			if (value is string) {
				var str = (string)value;
				if (type == typeof(Rect)) {
					var m = Regex.Match(str, @"^{({.*?}),({.*?})}$");
					var p = (Vector2)ChangeType(m.Groups[1].Value, typeof(Vector2), culture);
					var s = (Vector2)ChangeType(m.Groups[2].Value, typeof(Vector2), culture);
					return new Rect(p, s);
				}
				else if (type == typeof(Vector2)) {
					var m = Regex.Match(str, @"^{(.*?),(.*?)}$");
					var w = float.Parse(m.Groups[1].Value);
					var h = float.Parse(m.Groups[2].Value);
					return new Vector2(w, h);
				}
			}
			else if (typeof(IDictionary).IsAssignableFrom(value.GetType())) {
				var dict = (IDictionary)value;
				if (type == typeof(Rect)) {
					var x = float.Parse((string)dict["x"]);
					var y = float.Parse((string)dict["y"]);
					var w = float.Parse((string)dict["w"]);
					var h = float.Parse((string)dict["h"]);
					return new Rect(x, y, w, h);
				}
				else if (type == typeof(Vector2)) {
					var w = float.Parse((string)dict["w"]);
					var h = float.Parse((string)dict["h"]);
					return new Vector2(w, h);
				}
			}
			return base.ChangeType(value, type, culture);
		}
	}
}
