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

namespace Cryville.Crtr.Ruleset {
	internal struct JudgeActionResult {
		public bool BreakExecution;
		public bool PreventRecycle;
	}
	public abstract class JudgeAction {
		public static JudgeAction Construct(HashSet<string> a, string k) {
			if (a.Remove("pass")) {
				return new Pass(a, from i in k.Split(',') select new Identifier(i.Trim()));
			}
			else if (a.Remove("call")) {
				return new Call(a, new Identifier(k));
			}
			else if (a.Remove("score")) {
				return new Score(a, k);
			}
			else if (a.Remove("var")) {
				return new Variable(a, new Identifier(k));
			}
			throw new FormatException("Invalid judge action format.");
		}
		public readonly HashSet<string> annotations;
		public JudgeAction(IEnumerable<string> a) {
			annotations = a.ToHashSet();
		}
		public virtual void Optimize(PdtEvaluatorBase etor, PdtExpression value) { etor.Optimize(value); }
		internal abstract JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index);
		public class Call : JudgeAction {
			readonly Identifier _target;
			public Call(IEnumerable<string> a, Identifier k) : base(a) {
				_target = k;
			}
			internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
				judge.Call(ev, time, _target, onMiss, index);
				return new JudgeActionResult { BreakExecution = true, PreventRecycle = true };
			}
		}
		public class Pass : JudgeAction {
			readonly Identifier[] _targets;
			public Pass(IEnumerable<string> a, IEnumerable<Identifier> k) : base(a) {
				_targets = k.ToArray();
			}
			internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
				return new JudgeActionResult { BreakExecution = judge.Pass(ev, time, _targets, onMiss, depth) };
			}
		}
		public class Score : JudgeAction {
			readonly ScoreOperation _op;
			public Score(IEnumerable<string> a, string k) : base(a) {
				_op = new ScoreOperation(k);
			}
			public Score(ScoreOperation op) : base(Enumerable.Empty<string>()) {
				_op = op;
			}
			public override void Optimize(PdtEvaluatorBase etor, PdtExpression value) {
				base.Optimize(etor, value);
				if (_op.op != default(Identifier)) PdtExpression.PatchCompound(_op.name.Key, _op.op.Key, value);
			}
			internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
				judge.UpdateScore(_op, exp);
				return new JudgeActionResult();
			}
		}
		public class Variable : JudgeAction {
			readonly Identifier _target;
			public Variable(IEnumerable<string> a, Identifier k) : base(a) {
				_target = k;
			}
			internal override JudgeActionResult Execute(IJudge judge, JudgeEvent ev, float time, PdtExpression exp, bool onMiss, int depth, int index) {
				// throw new NotImplementedException();
				return new JudgeActionResult();
			}
		}
	}
}
