using System;
using System.Collections.Generic;
using System.Text;

namespace Cryville.Common.Pdt {
	/// <summary>
	/// PDT expression.
	/// </summary>
	public class PdtExpression {
		internal LinkedList<PdtInstruction> Instructions;
		/// <summary>
		/// Whether the value of this expression is constant.
		/// </summary>
		/// <remarks>The value of this property is <c>false</c> until it is optimized.</remarks>
		public bool IsConstant { get; internal set; }
		internal bool IsPotentialConstant;
		internal PdtExpression(LinkedList<PdtInstruction> ins) {
			Instructions = ins;
		}
		public override string ToString() {
			string r = "";
			bool flag = false;
			foreach (PdtInstruction ins in Instructions) {
				if (flag) r += "; ";
				r += ins.ToString();
				flag = true;
			}
			return r;
		}
	}
	internal abstract class PdtInstruction {
		internal abstract void Execute(PdtEvaluatorBase etor);
		public class PushConstant : PdtInstruction {
			public int Type { get; private set; }
			public byte[] Value { get; private set; }
			public PushConstant(int type, byte[] value) {
				Type = type;
				Value = value;
			}
			public PushConstant(int type, byte[] buffer, int offset, int len) {
				Type = type;
				Value = new byte[len];
				Array.Copy(buffer, offset, Value, 0, len);
			}
			internal override void Execute(PdtEvaluatorBase etor) {
				etor.PushConstant(Type, Value);
			}
			public override string ToString() {
				return string.Format("pushc ({0:x8}){1}", Type, BitConverter.ToString(Value));
			}
		}
		public class PushVariable : PdtInstruction {
			public int Name { get; private set; }
			public PushVariable(int name) { Name = name; }
			public PushVariable(string name) : this(IdentifierManager.SharedInstance.Request(name)) { }
			internal override void Execute(PdtEvaluatorBase etor) {
				etor.PushVariable(Name);
			}
			public override string ToString() {
				return string.Format("pushv {0}", IdentifierManager.SharedInstance.Retrieve(Name));
			}
		}
		public class Operate : PdtInstruction {
			public PdtOperatorSignature Signature { get; private set; }
			public Operate(int name, int paramCount) {
				Signature = new PdtOperatorSignature(name, paramCount);
			}
			public Operate(string name, int paramCount) {
				Signature = new PdtOperatorSignature(name, paramCount);
			}
			internal override void Execute(PdtEvaluatorBase etor) {
				etor.Operate(Signature);
			}
			public override string ToString() {
				return string.Format("op {0}", Signature);
			}
		}
		public class Collapse : PdtInstruction {
			public int Name { get; private set; }
			public LinkedListNode<PdtInstruction> Target { get; internal set; }
			public Collapse(string name, LinkedListNode<PdtInstruction> target) {
				Name = IdentifierManager.SharedInstance.Request(name);
				Target = target;
			}
			internal override void Execute(PdtEvaluatorBase etor) {
				etor.Collapse(Name, Target);
			}
			public override string ToString() {
				return string.Format("col {0}{{{1}}}", IdentifierManager.SharedInstance.Retrieve(Name), Target.Value);
			}
		}
	}
	public partial class PdtInterpreter<T> {
		readonly static Dictionary<char, int> OP_PRIORITY = new Dictionary<char, int> {
			{ '@', 7 },
			{ '*', 6 }, { '/', 6 }, { '%', 6 },
			{ '+', 5 }, { '-', 5 },
			{ '=', 4 }, { '<', 4 }, { '>', 4 },
			{ '!', 3 },
			{ '&', 2 },
			{ '|', 1 },
			{ ',', 0 },
			{ '$', -1 },
		};
		readonly static Dictionary<char, int> OP_TYPE = new Dictionary<char, int> {
			{ '@', 0 },
			{ '*', 0 }, { '/', 0 }, { '%', 0 },
			{ '+', 0 }, { '-', 0 },
			{ '=', 0 }, { '<', 0 }, { '>', 0 },
			{ '!', 0 },
			{ '&', 1 },
			{ '|', 1 },
			{ ',', -2 },
			{ '$', -1 },
		};

		readonly static PdtExpression _emptyexp;
		static PdtInterpreter() {
			var ins = new LinkedList<PdtInstruction>();
			ins.AddLast(new PdtInstruction.PushConstant(
				PdtInternalType.Number, BitConverter.GetBytes(1f)
			));
			_emptyexp = new PdtExpression(ins);
		}

		PdtExpToken GetToken() {
			ws();
			var result = new PdtExpToken {
				Type = ct & 0x0fe0
			};
			switch (result.Type) {
				case 0x0020: result.Value = GetIdentifier(); break;
				case 0x0040: result.Value = GetNumber(); break;
				case 0x0100: result.Value = GetString(); break;
				default: result.Value = cc.ToString(); Position++; break;
			}
			return result;
		}
		private struct PdtExpToken {
			public int Type { get; set; }
			public string Value { get; set; }
			public override string ToString() {
				return string.Format("0x{0:x4}: {1}", Type, Value);
			}
			public readonly static PdtExpToken EmptyOperator = new PdtExpToken {
				Type = 0x0080,
				Value = "$",
			};
		}

		/// <summary>
		/// Interprets an expression from the current position and loads it onto an instruction set.
		/// </summary>
		/// <param name="ins">The instruction set.</param>
		/// <param name="enc">
		///		<para>The enclosing scope type of the (sub)expression. May be one of the following:</para>
		///		<list type="bullet">
		///			<item><term><c>-2</c></term><description>The expression is in a root scope which ends at a semicolon.</description></item>
		///			<item><term><c>-1</c></term><description>The expression is in a bracketed scope which ends at a closing parenthesis.</description></item>
		///			<item><term>Any non-negative values</term><description>The expression is in an auxiliary scope which ends before a closing parenthesis or an operator whose priority is lower (less) than or equal to the value of this parameter.</description></item>
		///		</list>
		/// </param>
		/// <param name="pc">The parameter count in this (sub)expression.</param>
		/// <param name="token">The token already parsed but required for the interpretation of the subexpression.</param>
		/// <returns>The expression token following this (sub)expression.</returns>
		PdtExpToken InterpretExp(LinkedList<PdtInstruction> ins, int enc, out int pc, PdtExpToken? token = null) {
			PdtExpToken t1, t2;
			int insc0 = ins.Count;
			Dictionary<LinkedListNode<PdtInstruction>, string> colp =  null;
			pc = 1;
			if (token == null) t1 = PdtExpToken.EmptyOperator;
			else {
				t1 = token.Value;
				if (OP_TYPE[t1.Value[0]] == 1)
					colp = new Dictionary<LinkedListNode<PdtInstruction>, string> { { ins.Last, token.Value.Value } };
			}
			while (true) {
				t2 = InterpretExpBlock(ins);
			panic:
				switch (t2.Type) {
					case 0x0080:
						if (OP_TYPE[t1.Value[0]] != -1) {
							int p1 = OP_PRIORITY[t1.Value[0]];
							int p2 = OP_PRIORITY[t2.Value[0]];
							if (p2 <= enc) goto exit;
							if (p2 > p1) {
								int _;
								t2 = InterpretExp(ins, p1, out _, t2);
								goto panic;
							}
							switch (OP_TYPE[t1.Value[0]]) {
								case 0: ins.AddLast(new PdtInstruction.Operate(t1.Value, 2)); break;
								case 1: colp.Add(ins.Last, t2.Value); break;
								default: pc++; break;
							}
						}
						else if (OP_TYPE[t2.Value[0]] == 1) {
							colp = new Dictionary<LinkedListNode<PdtInstruction>, string> { { ins.Last, t2.Value } };
						}
						t1 = t2;
						break;
					case 0x0400:
						if (enc == -2) throw new FormatException("Expression not enclosed correctly: Too many closing brackets");
						if (ins.Count == insc0) pc = 0;
						goto exit;
					case 0x0800:
						goto exit;
				}
			}
		exit:
			switch (OP_TYPE[t1.Value[0]]) {
				case 0: ins.AddLast(new PdtInstruction.Operate(t1.Value, 2)); break;
				case 1:
					foreach (var p in colp)
						ins.AddAfter(p.Key, new PdtInstruction.Collapse(p.Value, ins.Last));
					break;
				case -2: pc++; break;
			}
			return t2;
		}

		/// <summary>
		/// Interprets an expression block which consists of several adjacent constants and subexpressions, optionally with a unary operator.
		/// </summary>
		/// <param name="ins">The instruction set.</param>
		/// <returns>The expression token following this expression block.</returns>
		PdtExpToken InterpretExpBlock(LinkedList<PdtInstruction> ins) {
			var t = GetToken();
			if (t.Type == 0x0080) {
				var r = InterpretExpBlock(ins);
				ins.AddLast(new PdtInstruction.Operate(t.Value, 1));
				return r;
			}
			bool flag = false;
			PdtExpToken? buf = null;
			while (true) {
				if (buf != null && t.Type != 0x0200) {
					PdtExpression def;
					if (defs.TryGetValue(buf.Value.Value, out def)) {
						foreach (var i in def.Instructions) ins.AddLast(i);
					}
					else ins.AddLast(new PdtInstruction.PushVariable(buf.Value.Value));
					buf = null;
					TryPushAdjMul(ins, ref flag);
				}
				switch (t.Type) {
					case 0x0020:
						buf = t;
						break;
					case 0x0040:
						float num = float.Parse(t.Value);
						ins.AddLast(new PdtInstruction.PushConstant(PdtInternalType.Number, BitConverter.GetBytes(num)));
						break;
					case 0x0100:
						int strlen = t.Value.Length;
						unsafe {
							var strbuf = new byte[strlen * sizeof(char) + sizeof(int)];
							fixed (byte* psuffix = strbuf) {
								*(int*)psuffix = strlen;
							}
							Encoding.Unicode.GetBytes(t.Value, 0, strlen, strbuf, sizeof(int));
							ins.AddLast(new PdtInstruction.PushConstant(PdtInternalType.String, strbuf));
						}
						break;
					case 0x0200:
						int pc;
						InterpretExp(ins, -1, out pc);
						if (buf != null) {
							ins.AddLast(new PdtInstruction.Operate(buf.Value.Value, pc));
							buf = null;
						}
						else if (pc > 1) {
							ins.AddLast(new PdtInstruction.Operate(",", pc));
						}
						else if (pc == 0)
							throw new FormatException("Empty subexpression");
						break;
					default:
						return t;
				}
				if (buf == null) TryPushAdjMul(ins, ref flag);
				t = GetToken();
			}
		}
		void TryPushAdjMul(LinkedList<PdtInstruction> ins, ref bool flag) {
			if (flag) ins.AddLast(new PdtInstruction.Operate("*", 2));
			else flag = true;
		}
	}
}
