using Cryville.Common; using Cryville.Common.Pdt; using Cryville.Crtr.Skin.Components; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using UnityEngine; using CAnchor = Cryville.Crtr.Anchor; namespace Cryville.Crtr.Skin { public class SkinSelectors { readonly SkinSelector[] selectors; public readonly HashSet annotations; public SkinSelectors(IEnumerable s, IEnumerable a) { selectors = s.ToArray(); annotations = a.ToHashSet(); } public override string ToString() { if (selectors.Length == 0) return ""; bool flag = false; string r = ""; foreach (var a in annotations) { if (flag) r += " @" + a; else { r += "@" + a; flag = true; } } foreach (var s in selectors) { if (flag) r += " " + s.ToString(); else { r += s.ToString(); flag = true; } } return r; } public void Optimize(PdtEvaluatorBase etor) { for (int i = 0; i < selectors.Length; i++) { selectors[i].Optimize(etor); } } public IEnumerable MatchStatic(ISkinnableGroup g, SkinContext ctx) { IEnumerable result = new SkinContext[] { ctx }; foreach (var s in selectors) { result = result.SelectMany(l => s.MatchStatic(g, l)); } return result; } public bool IsUpdatable(ISkinnableGroup g, int dl) { foreach (var s in selectors) if (!s.IsUpdatable(g, dl)) return false; return true; } public SkinContext MatchDynamic(ISkinnableGroup g, SkinContext ctx) { foreach (var s in selectors) { ctx = s.MatchDynamic(g, ctx); if (ctx == null) return null; } return ctx; } } public abstract class SkinSelector { protected SkinSelector() { } public abstract override string ToString(); public virtual void Optimize(PdtEvaluatorBase etor) { } public virtual IEnumerable MatchStatic(ISkinnableGroup g, SkinContext c) { throw new SelectorNotAvailableException(); } public virtual SkinContext MatchDynamic(ISkinnableGroup g, SkinContext c) { throw new SelectorNotAvailableException(); } public virtual bool IsUpdatable(ISkinnableGroup g, int dl) { return true; } public class CreateObject : SkinSelector { public CreateObject() { } public override string ToString() { return "$"; } public override IEnumerable MatchStatic(ISkinnableGroup g, SkinContext c) { var obj = new GameObject("__obj__"); obj.transform.SetParent(c.Transform, false); obj.AddComponent(); return new SkinContext[] { new(obj.transform) }; } } public class Anchor : SkinSelector { public int Name { get; private set; } public Anchor(string name) { Name = IdentifierManager.Shared.Request(name); } public override string ToString() { return string.Format(".{0}", IdentifierManager.Shared.Retrieve(Name)); } public override IEnumerable MatchStatic(ISkinnableGroup g, SkinContext c) { if (g.TryGetAnchorsByName(Name, out IReadOnlyCollection anchors)) { return anchors.Select(a => a.SkinContext); } else return Enumerable.Empty(); } } public class AtAnchor : SkinSelector { public int Name { get; private set; } public AtAnchor(string name) { Name = IdentifierManager.Shared.Request(name); } public override string ToString() { return string.Format("..{0}", IdentifierManager.Shared.Retrieve(Name)); } public override SkinContext MatchDynamic(ISkinnableGroup g, SkinContext c) { return g.OpenedAnchorName == Name ? c : null; } public override bool IsUpdatable(ISkinnableGroup g, int dl) { return dl >= g.AtAnchorDynamicLevel; } } public class Property : SkinSelector { readonly PdtExpression _exp; readonly PdtOperator _op; bool _flag; public Property(PdtExpression exp) { _exp = exp; _op = new PropOp.Boolean(v => _flag = v); } public override string ToString() { return string.Format("> {{{0}}}", _exp); } public override void Optimize(PdtEvaluatorBase etor) { etor.Optimize(_exp); } public override IEnumerable MatchStatic(ISkinnableGroup g, SkinContext c) { var result = Match(c); if (result != null) return new SkinContext[] { result }; else return Enumerable.Empty(); } public override SkinContext MatchDynamic(ISkinnableGroup g, SkinContext c) { return Match(c); } public SkinContext Match(SkinContext a) { PdtEvaluator.Instance.ContextTransform = a.Transform; try { if (!PdtEvaluator.Instance.Evaluate(_op, _exp)) throw new EvaluationFailureException(); return _flag ? a : null; } catch (Exception ex) { throw new SelectorNotAvailableException("The expression is not evaluatable under the current context", ex); } finally { PdtEvaluator.Instance.ContextTransform = null; } } } public class ElementType : SkinSelector { readonly string _type; public ElementType(string type) { _type = type; } public override string ToString() { return _type; } public override IEnumerable MatchStatic(ISkinnableGroup g, SkinContext c) { return g.TypeName == _type ? new SkinContext[] { c } : Enumerable.Empty(); } } } public class SelectorNotAvailableException : Exception { public SelectorNotAvailableException() : base("The selector is not available under the current context") { } public SelectorNotAvailableException(string message) : base(message) { } public SelectorNotAvailableException(string message, Exception innerException) : base(message, innerException) { } protected SelectorNotAvailableException(SerializationInfo info, StreamingContext context) : base(info, context) { } } }