using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AR;
namespace Project.Commands
{
	public class CommandBuffer
	{
		public int Idx { get; set; }

		private int REGISTER_VALUE = 0;
		private Boolean REGISTER_TRUE = false;
		private Boolean REGISTER_FALSE = false;
		private Boolean REGISTER_EQUAL = false;
		private Boolean REGISTER_ABOVE = false;
		private Boolean REGISTER_BELOW = false;


		/// <summary>
		/// 순차실행명령
		/// </summary>
		public List<Command> Commands;

		/// <summary>
		/// 상시실행명령
		/// </summary>
		public List<Command> SPS;
		public CommandBuffer()
		{
			Commands = new List<Command>();
			SPS = new List<Command>();
		}
		public void AddSeq(Command cmd)
		{
			cmd.Idx = this.Commands.Count;
			this.Commands.Add(cmd);
		}
		public void AddSPS(Command cmd)
		{
			cmd.Idx = this.SPS.Count;
			this.SPS.Add(cmd);
		}
		public void Clear()
		{
			Commands.Clear();
			SPS.Clear();
			Idx = 0;
		}
		public StepResult Run()
		{
			//sps는 모두 실행한다
			StepResult rlt;
			foreach (var sps in this.SPS)
			{
				rlt = RunCode(sps);
				if (rlt == StepResult.Wait) return StepResult.Wait; //SPS에서 대기 코드가 있다
				else if (rlt == StepResult.Error) return StepResult.Error;
			}

			//sequece 는 현재 것만 실행한다.
			if (Idx < 0) Idx = 0;
			var cmd = this.Commands[Idx];
			rlt = RunCode(cmd);
			if (rlt == StepResult.Complete) //이 명령이 완료되면 다음으로 진행한다
			{
				Idx += 1;
				if (Idx >= this.Commands.Count) return StepResult.Complete;
				else return StepResult.Wait;
			}
			return rlt;
		}

		private StepResult RunCode(Command cmd)
		{
			switch (cmd.type)
			{
				case CType.NOP: return StepResult.Complete;
				case CType.Wait:
					var data0 = cmd.Data as CDWait;
					if (data0.Trigger == false)
					{
						//아직 시작을 안했으니 시작시키고 대기한다
						data0.SetTrigger(true);
						return StepResult.Wait;
					}
					else
					{
						//아직 시간을 다 쓰지 않았다면 넘어간다
						if (data0.TimeOver == false) return StepResult.Wait;
					}
					break;
				case CType.Output:
					var data1 = cmd.Data as CDOutput;
					if (data1.PinIndex < 0) return StepResult.Error;
					if (DIO.SetOutput((eDOName)data1.Pin, data1.Value) == false) return StepResult.Error;
					break;
				case CType.Move:
					var data2 = cmd.Data as CDMove;
					MOT.Move((eAxis)data2.Axis, data2.Position, data2.Speed, data2.Acc, data2.Relative);
					break;
				case CType.MoveForece:
					var data3 = cmd.Data as CDMove;
					MOT.Move((eAxis)data3.Axis, data3.Position, data3.Speed, data3.Acc, data3.Relative, false, false);
					break;
				case CType.MoveWait:
					var data4 = cmd.Data as CDMove;
					var axis = (eAxis)data4.Axis;
					var mrlt = MOT.CheckMotionPos(axis, new TimeSpan(1), data4.Position, data4.Speed, data4.Acc, data4.Dcc, cmd.description);
					if (mrlt == false) return StepResult.Wait;
					break;
				case CType.GetFlag:
					var data5 = cmd.Data as CDFlag;
					data5.Value = PUB.flag.get(data5.Flag);
					REGISTER_FALSE = data5.Value == false;
					REGISTER_TRUE = data5.Value == true;
					break;
				case CType.SetFlag:
					var data6 = cmd.Data as CDFlag;
					PUB.flag.set(data6.Flag, data6.Value, cmd.description);
					break;
				case CType.True:
					var data7 = cmd.Data as CDCommand;
					if (REGISTER_TRUE) return RunCode(data7.Command);
					break;
				case CType.False:
					var data8 = cmd.Data as CDCommand;
					if (REGISTER_FALSE) return RunCode(data8.Command);
					break;
				case CType.GetVar:  //공용변수의값
					var data10 = cmd.Data as CDGetVar;
					if (data10.Key == "STEPTIME")
					{
						data10.Confirm = true;
						data10.Value = (int)PUB.sm.StepRunTime.TotalMilliseconds;
					}


					break;
				case CType.GetSetVar:   //공용변수(설정)의 값
					var data11 = cmd.Data as CDGetVar;
					if (data11.Key == "TIMEOUT_HOMEFULL")
					{
						data11.Confirm = true;
						data11.Value = 60;// (int)Pub.sm.StepRunTime.TotalMilliseconds;
					}
					break;

				case CType.Compare:

					var data9 = cmd.Data as CDCompare<int>;
					if (data9 != null)
					{
						RunCode(data9.Source);    //비교값(좌)
						RunCode(data9.Target);    //비교값(우)

						var valS = data9.Source.Data as ICommandValue;
						var valT = data9.Target.Data as ICommandValue;
						int ValueS = (int)valS.Value;
						int ValueT = (int)valT.Value;

						REGISTER_ABOVE = ValueS > ValueT;
						REGISTER_BELOW = ValueS < ValueT;
						REGISTER_EQUAL = ValueS == ValueT;
						REGISTER_TRUE = ValueS == ValueT;
						REGISTER_FALSE = ValueS != ValueT;
						REGISTER_VALUE = ValueS - ValueT;
					}
					else return StepResult.Error;


					break;

				case CType.SetError:
					var data12 = cmd.Data as CDError;
					PUB.Result.SetResultMessage(data12.ResultCode, data12.ErrorCode, data12.NextStep);
					break;
			}

			return StepResult.Complete;
		}
	}
	public enum CType
	{
		NOP = 0,
		/// <summary>
		/// motion move
		/// </summary>
		Move,
		MoveForece,

		/// <summary>
		/// move and wait
		/// </summary>
		MoveWait,

		/// <summary>
		/// set digital output
		/// </summary>
		Output,
		Log,
		StepChange,
		/// <summary>
		/// check digital input
		/// </summary>
		InputCheck,

		/// <summary>
		/// check digital output
		/// </summary>
		OutputCheck,

		GetFlag,
		SetFlag,

		Equal,
		NotEqual,
		True,
		False,
		Zero,
		NonZero,
		SetError,
		Compare,
		SetVar,
		GetVar,
		GetSetVar,
		Above,
		Below,

		Wait,
		Run,

	}
	public class Command
	{

		public CType type { get; set; } = CType.NOP;
		public int Idx { get; set; }
		public string description { get; set; }
		public ICommandData Data { get; set; }
		public Command(CType type, string desc = "")
		{
			this.type = type;
			this.description = desc;
		}
	}
	public interface ICommandData
	{
		//  string Description { get; set; }
	}
	public interface ICommandValue
	{
		object Value { get; set; }
	}
	public class CDGetVar : ICommandData, ICommandValue
	{
		public string Key { get; set; }
		public object Value { get; set; }
		public Boolean Confirm { get; set; }
		public CDGetVar(string key)
		{
			this.Key = key;
		}
	}
	public class CDGetSetVar : ICommandData, ICommandValue
	{
		public string Key { get; set; }
		public object Value { get; set; }
		public Boolean Confirm { get; set; }
		public CDGetSetVar(string key)
		{
			this.Key = key;
		}
	}
	public class CDSetVar : ICommandData
	{
		public string Key { get; set; }
		public int Value { get; set; }
		public CDSetVar(string key, int value)
		{
			this.Key = key;
			this.Value = Value;
		}
	}
	public class CDFlag : ICommandData
	{
		public eVarBool Flag { get; set; }
		public Boolean Value { get; set; }
		public CDFlag(eVarBool flag)
		{
			this.Flag = flag;
			Value = false;
		}
	}

	public class CDSetValI : ICommandData
	{
		public int Value { get; set; }
		public CDSetValI(int idx, int value)
		{
			this.Value = value;
		}
	}
	public class CDSetValB : ICommandData
	{
		public bool Value { get; set; }
		public CDSetValB(int idx, bool value)
		{
			this.Value = value;
		}
	}
	public class CDCommand : ICommandData
	{
		public Command Command { get; set; }
		public CDCommand(Command command)
		{
			this.Command = command;
		}
	}
	public class CDError : ICommandData
	{
		public eResult ResultCode { get; set; }
		public eECode ErrorCode { get; set; }
		public eNextStep NextStep { get; set; }

		public CDError(eResult resultCode, eECode errorCode, eNextStep nextStep)
		{
			ResultCode = resultCode;
			ErrorCode = errorCode;
			NextStep = nextStep;
		}
	}
	//public class CDCompare<T> : ICommandData
	//{
	//    public T Value { get; set; }
	//    public CDCompare(T value)
	//    {
	//        Value = value;
	//    }
	//    public CDCompare(Command source, Command target)
	//    {
	//        Value = value;
	//    }
	//}
	public class CDCompare<T> : ICommandData
	{
		public Command Source { get; set; }
		public Command Target { get; set; }
		public CDCompare(Command source, Command target)
		{
			Source = source;
			Target = target;
		}
	}

	public class CDWait : ICommandData
	{
		public int WaitMS { get; set; }
		public DateTime StartTime { get; set; }
		public Boolean Trigger { get; set; }
		private TimeSpan GetTime { get { return DateTime.Now - StartTime; } }
		public Boolean TimeOver { get { return GetTime.TotalMilliseconds > WaitMS; } }
		public void SetTrigger(Boolean value)
		{
			Trigger = value;
			StartTime = DateTime.Now;
		}

		public CDWait() : this(100) { }
		public CDWait(int ms)
		{
			this.WaitMS = ms;
		}
	}
	public class CDMove : ICommandData
	{

		public int Axis { get; set; }
		public double Position { get; set; }
		public double Speed { get; set; }
		public double Acc { get; set; }
		public double Dcc { get; set; }
		public Boolean Relative { get; set; }
		//  public string Description { get; set; }
		public CDMove(eAxis axis, double pos, double speed) : this((int)axis, pos, speed, 0) { }
		public CDMove(int axis, double pos, double speed, double acc, double dcc = 0, Boolean relatvie = false)
		{
			Axis = axis;
			Position = pos;
			Speed = speed;
			Acc = acc;
			Dcc = dcc == 0 ? acc : dcc;
			Relative = relatvie;

		}
	}
	public class CDOutput : ICommandData
	{

		public eDOName Pin { get; set; }
		public int PinIndex { get; set; }
		public Boolean Value { get; set; }
		//  public string Description { get; set; }
		//public CDOutput(eDOName pin) : this(pin, false) { }
		public CDOutput(eDOName pin, Boolean value)
		{
			Pin = pin;
			PinIndex = (int)pin;
			Value = value;
		}
		public CDOutput(int point, Boolean value)
		{
			Pin = (eDOName)point;
			PinIndex = point;
			Value = value;
		}
	}

	public class CDRun : ICommandData
	{
		public Action Target { get; set; }
		public CDRun(Action target)
		{
			Target = target;
		}
	}
	public class CDRunRet<T> : ICommandData
	{
		public Func<T> Target { get; set; }
		public CDRunRet(Func<T> target)
		{
			Target = target;
		}
	}

	public class CDLog : ICommandData
	{

		public string Message { get; set; }
		public Boolean IsError { get; set; }
		public CDLog(string message, Boolean err = false)
		{
			Message = message;
			IsError = err;
		}
	}
	public class CDStep : ICommandData
	{

		public eSMStep Step { get; set; }
		public Boolean Force { get; set; }
		public CDStep(eSMStep step, Boolean force = false)
		{
			Step = step;
			Force = force;
		}
	}
}
