using Cryville.Common.Pdt;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Cryville.Crtr.Skin {
	public class SkinInterpreter : PdtInterpreter {
		public SkinInterpreter(string src, Binder binder) : base(src, binder) { }

		readonly List<SkinSelector> s = new List<SkinSelector>();
		readonly HashSet<string> a = new HashSet<string>();
		readonly List<string> k = new List<string>(2);
		protected override object InterpretKey(Type type) {
			if (typeof(SkinElement).IsAssignableFrom(type))
				return InterpretSkinElementKey();
			else if (typeof(AnimationSpan).IsAssignableFrom(type))
				return InterpretAnimationSpanKey();
			else
				return base.InterpretKey(type);
		}
		object InterpretSkinElementKey() {
			s.Clear(); a.Clear(); k.Clear();
			bool invalidKeyFlag = false, compKeyFlag = false;
			while (true) {
				int pp = Position;
				switch (cc) {
					case '@':
						GetChar();
						a.Add(GetIdentifier());
						break;
					case '$':
						GetChar();
						s.Add(new SkinSelector.CreateObject());
						invalidKeyFlag = true;
						break;
					case '.':
						GetChar();
						if (cc == '.') {
							GetChar();
							s.Add(new SkinSelector.AtAnchor(GetIdentifier()));
							invalidKeyFlag = true;
						}
						else {
							var p3 = GetIdentifier();
							s.Add(new SkinSelector.Anchor(p3));
							if (!invalidKeyFlag) {
								if (k.Count != 1) invalidKeyFlag = true;
								else k.Add(p3);
							}
						}
						break;
					case '>':
						GetChar();
						s.Add(new SkinSelector.Property(GetExp()));
						break;
					case ';':
					case ':':
						if (invalidKeyFlag) throw new FormatException("Invalid key format");
						return SkinPropertyKey.Construct(a, k, compKeyFlag);
					case '{':
						return new SkinSelectors(s, a);
					case '}':
						throw new FormatException("Invalid token");
					case '*':
						GetChar();
						compKeyFlag = true;
						break;
					default:
						var p4 = GetIdentifier();
						s.Add(new SkinSelector.ElementType(p4));
						if (!invalidKeyFlag) {
							if (k.Count != 0) invalidKeyFlag = true;
							else k.Add(p4);
						}
						break;
				}
				ws();
				if (Position == pp) throw new FormatException("Invalid selector or key format");
			}
		}
		object InterpretAnimationSpanKey() {
			if ((ct & 0x0040) != 0 || cc == ',') {
				float start = 0, end = 1;
				if (cc != ',') {
					start = float.Parse(GetNumber());
					ws(); if (cc != ',') throw new FormatException("Invalid span format");
				}
				GetChar(); ws();
				if (cc != '{') end = float.Parse(GetNumber());
				ws();
				if (cc != '{') throw new FormatException("Invalid span format");
				return new Clip(start, end);
			}
			a.Clear(); k.Clear();
			while (true) {
				int pp = Position;
				switch (cc) {
					case '.':
						GetChar();
						if (k.Count != 1)
							throw new FormatException("Invalid key format");
						k.Add(GetIdentifier());
						break;
					case ';':
					case ':':
						return SkinPropertyKey.Construct(a, k, false);
					case '{':
						throw new FormatException("Invalid token");
					case '}':
						throw new FormatException("Invalid token");
					default:
						if (k.Count != 0)
							throw new FormatException("Invalid key format");
						k.Add(GetIdentifier());
						break;
				}
				ws();
				if (Position == pp) throw new FormatException("Invalid selector or key format");
			}
		}
	}
}
