using Cryville.Common;
using Cryville.Common.Pdt;
using Cryville.Crtr.Components;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;

namespace Cryville.Crtr {
	public class Skin {
		[JsonRequired]
		public long format;

		public string @base;

		[JsonRequired]
		public string ruleset;
		
		[JsonRequired]
		public string pdt;

		[JsonIgnore]
		public PdtSkin Root { get; private set; }

		public void LoadPdt(DirectoryInfo dir) {
			using (StreamReader pdtreader = new StreamReader(dir.FullName + "/" + pdt + ".pdt", Encoding.UTF8)) {
				var src = pdtreader.ReadToEnd();
				Root = new SkinInterpreter(src, null).Interpret();
			}
		}
	}

	public class PdtSkin : SkinElement { }

	[Binder(typeof(SkinElementBinder))]
	public class SkinElement {
		[ElementList]
		public Dictionary<SkinSelectors, SkinElement> elements
			= new Dictionary<SkinSelectors, SkinElement>();

		[PropertyList]
		public Dictionary<SkinPropertyKey, PdtExpression> properties
			= new Dictionary<SkinPropertyKey, PdtExpression>();

		public bool IsDynamic {
			get;
			private set;
		}

		public void Optimize(PdtEvaluatorBase etor) {
			IsDynamic = true;
			foreach (var e in properties.Values) {
				etor.Optimize(e);
				if (!e.IsConstant)
					IsDynamic = true;
			}
			foreach (var e in elements) {
				e.Key.Optimize(etor);
				e.Value.Optimize(etor);
				if (e.Value.IsDynamic)
					IsDynamic = true;
			}
		}
	}
	public struct SkinPropertyKey {
		public Type Component;
		public string Name;
	}
	public class SkinElementBinder : EmptyBinder {
		public override object ChangeType(object value, Type type, CultureInfo culture) {
			if (value is string && type == typeof(SkinPropertyKey)) {
				var cp = ((string)value).Split('.');
				switch (cp.Length) {
					case 1:
						var key = cp[0];
						if (key[0] == '*')
							return new SkinPropertyKey { Component = GetComponentByName(key.Substring(1)) };
						else
							return new SkinPropertyKey { Component = typeof(TransformInterface), Name = key };
					case 2:
						return new SkinPropertyKey { Component = GetComponentByName(cp[0]), Name = cp[1] };
				}
			}
			return base.ChangeType(value, type, culture);
		}

		static readonly char[] nssep = new char[]{':'};
		Type GetComponentByName(string name) {
			var nstuple = name.Split(nssep, 2);
			var ns = nssep.Length == 2 ? nstuple[0] : "generic";
			name = nssep.Length == 2 ? nstuple[1] : nstuple[0];
			if (ns == "generic")
				return GenericResources.Components[name];
			throw new ArgumentException(string.Format("Component type {0} not found", name));
		}
	}
}
