#pragma warning disable IDE1006 // 명명 스타일
using arDev.MOT;
using AR;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Project
{
	public static partial class MOT
	{

		/// <summary>
		/// 상대위치로 모터를 이동합니다
		/// </summary>
		/// <param name="value"></param>
		/// <param name="src"></param>
		public static void Rotation(double value,string src)
		{
			//모터회전
			var thpos = MOT.GetPTPos(ePTLoc.READY); //적용할 속도값용
			MOT.Move(eAxis.Z_THETA, value, thpos.Speed, thpos.Acc, true);
			PUB.log.AddI($"Theta Rorate:{value},Reason:{src}");
		}

		public static Boolean CheckMotionLimitError()
		{
			for (short i = 0; i < PUB.mot.DeviceCount; i++)
			{
				if (PUB.mot.IsUse(i) == false) continue; //미사용축은 제외한다.
														 //if (SETTING.System.UseAxis(i) == false) continue;
														 //if (SETTING.System.UseOriginSignal(i) == false) continue;
				if (PUB.mot.IsLimit(i)) return true;
			}
			return false;

		}

		#region "Check Motion Position"

		public static Boolean CheckMotionPos(TimeSpan stepTime, sPositionData posdata, string source, Boolean useInterLocak = true, int timeoutSec = 0)
		{
			if (posdata.Axis < 0) throw new Exception("CheckMotionPos:Motion number not found");
			//return CheckMotionPos((eAxis)posdata.Axis, stepTime, posdata.Position, posdata.Speed, posdata.Acc, posdata.Dcc, source, useInterLocak, timeoutSec, posdata.inpositionrange);
			return CheckMotionPos((eAxis)posdata.Axis, stepTime, posdata.Position, posdata.Speed, posdata.Acc, posdata.Dcc, source, useInterLocak, timeoutSec, posdata.inpositionrange);
		}


		public static Boolean CheckMotionPos(eAxis Motaxis, TimeSpan stepTime, double pos, double speed, double acc, double dcc, string source, Boolean useInterLocak = true, int timeoutSec = 0, float inpaccr = 0f)
		{
			var axis = (short)Motaxis;

			//타임아웃 적용 191213
			if (timeoutSec == 0) timeoutSec = AR.SETTING.Data.Timeout_MotionCommand;

			//accr 범위가 지정되잇다며 ㄴ그것을 사용한다
			if (inpaccr == 0f)
				inpaccr = 0.1f;// SETTING.System.INPAccurary(axis);


			//X축을 그립위치까지 이동함
			if (PUB.mot.IsInp(axis) && PUB.mot.IsMotion(axis) == false)
			{
				var offset = Math.Abs(PUB.mot.GetCmdPos(axis) - pos);
				var offsetReal = Math.Abs(PUB.mot.GetCmdPos(axis) - PUB.mot.GetActPos(axis));

				//커맨드위치는 오차가 없어야 한다
				if (offset > 0.001 || offsetReal > inpaccr) //220214 0.01)
				{
					//모션을 옴겨야하는 상황인데 최대시간을 초과했다면 오류로 처리한다
					if (timeoutSec != -1 && stepTime.TotalSeconds >= timeoutSec)
					{
						PUB.Result.SetResultTimeOutMessage(Motaxis, eECode.MOT_CMD, eNextStep.PAUSE, source, pos, PUB.mot.ErrorMessage);
						return false;
					}

					if (MOT.Move(Motaxis, pos, speed, acc, false, true, useInterLocak) == false)
					{
						Console.WriteLine("move error {0},pos={1} => {2}", Motaxis, pos, PUB.mot.ErrorMessage);
						return false;
					}

					//모션을 이동시켰으니 False 처리한다
					//return false;
				}
			}

			//축이 이동중이면 처리하지 않는다.
			if (PUB.mot.IsInp(axis) == false || PUB.mot.IsMotion(axis) == true)
			{
				return false;
			}

			//X축실위치값 확인
			if (MOT.getPositionMatch(axis, pos, inpaccr) == false)
			{
				return false;
			}


			return true;
		}


		#endregion


		#region "Get Axis Position"

		public static sPositionData GetPXPos(ePXLoc pos) { return GetAxPos(eAxis.PX_PICK, (int)pos); }
		public static sPositionData GetPZPos(ePZLoc pos) { return GetAxPos(eAxis.PZ_PICK, (int)pos); }
		public static sPositionData GetLMPos(eLMLoc pos) { return GetAxPos(eAxis.PL_MOVE, (int)pos); }
		public static sPositionData GetLZPos(eLZLoc pos) { return GetAxPos(eAxis.PL_UPDN, (int)pos); }
		public static sPositionData GetRMPos(eRMLoc pos) { return GetAxPos(eAxis.PR_MOVE, (int)pos); }
		public static sPositionData GetRZPos(eRZLoc pos) { return GetAxPos(eAxis.PR_UPDN, (int)pos); }
		public static sPositionData GetPTPos(ePTLoc pos) { return GetAxPos(eAxis.Z_THETA, (int)pos); }


		#endregion



		/// <summary>
		/// 지정된 축의 위치값에 속한 이름을 반환합니다.
		/// 특정 구간에 속하지 않은 경우 UNKNOWN 을 반환 합니다
		/// </summary>
		/// <param name="axis"></param>
		/// <param name="Pos"></param>
		/// <returns></returns>
		public static String GetPosName(eAxis axis, double Pos = -1, double CheckOffset = 0.1)
		{
			//홈을 잡지 않았다면 오류로 처리함\
			//eYPPosition retval = eYPPosition.Unknown;

			var motIndex = (short)axis;

			if (PUB.mot.IsInit == false) return "ERROR";// (T)ePickYPosition.ERROR; //200213
			if (PUB.mot.IsHomeSet(motIndex) == false) return "ERROR";//ePickYPosition.ERROR;
			if (PUB.Result == null || PUB.Result.isSetmModel == false) return "ERROR";//return ePickYPosition.ERROR;

			if (PUB.mot.IsUse(motIndex) == false) return "DISABLE";
			else if (PUB.mot.IsServAlarm(motIndex)) return "SVALM";
			else if (PUB.mot.IsServOn(motIndex) == false) return "SVOFF";
			else if (PUB.mot.IsLimitN(motIndex)) return "LIMITN";
			else if (PUB.mot.IsLimitP(motIndex)) return "LIMITP";
			else if (PUB.mot.IsLimitSWN(motIndex)) return "LIMITN(SW)";
			else if (PUB.mot.IsLimitSWP(motIndex)) return "LIMITP(SW)";
			else if (PUB.mot.IsOrg(motIndex)) return "HOME";
			else
			{
				//특정위치에있는지 확인한다.  match 명령사용
				if (Pos == -1) Pos = PUB.mot.GetActPos(motIndex);
				var PosT = 0.0;

				//해당 모션이 속한 좌표를 모두 가져온다
				var pts = PUB.Result.mModel.Position[motIndex].OrderBy(t => t.value).ToList();

				//각위치별값을 확인한다.
				for (int i = 0; i < pts.Count; i++)
				{
					var pValue = pts[i];
					if (Math.Abs(pValue.value - Pos) <= CheckOffset)
					{
						//해당위치에 있다i
						if (axis == eAxis.PX_PICK) return ((ePXLoc)i).ToString();
						else if (axis == eAxis.PZ_PICK) return ((ePZLoc)i).ToString();
						else if (axis == eAxis.Z_THETA) return ((ePTLoc)i).ToString();

						else if (axis == eAxis.PL_MOVE) return ((eLMLoc)i).ToString();
						else if (axis == eAxis.PL_UPDN) return ((eLZLoc)i).ToString();

						else if (axis == eAxis.PR_MOVE) return ((eRMLoc)i).ToString();
						else if (axis == eAxis.PR_UPDN) return ((eRZLoc)i).ToString();

						else return $"P#{i}";
					}
				}

				//위치를 찾지 못했다
				if (Math.Abs(Pos) <= CheckOffset) return "ZERO";
				else return "UNKNOWN";
			}
		}


		/// <summary>
		/// Z-L축이 안전위치에있는가?(Ready 보다 위에있으면 안전위치이다)
		/// </summary>
		/// <returns></returns>
		public static Boolean isAxisSaftyZone(eAxis axis, int allowoffset = 2)
		{
			//홈을 잡지 않았다면 오류로 처리함
			var motIndex = (short)axis;
			if (PUB.mot.IsInit == false) return false;
			if (PUB.mot.IsHomeSet(motIndex) == false) return false;
			if (PUB.Result == null || PUB.Result.isSetmModel == false) return false;

			sPositionData readypos;
			if (axis == eAxis.PX_PICK)
			{
				//피커는 안전위이가 2개 있다
				readypos = GetPXPos(ePXLoc.PICKON);
				//var readypos1 = GetAxPXPos(eAxisPXPos.ReadyR);
				//var offset1 = getPositionOffset(axis, readypos.position);
				//var offset2 = getPositionOffset(axis, readypos1.position);
				//if (offset1 < allowoffset || offset2 < allowoffset ) return true;    //오차가 2미만이라면 안전하다
				//return false;
			}
			else if (axis == eAxis.PZ_PICK) readypos = GetPZPos(ePZLoc.READY);
			else if (axis == eAxis.Z_THETA) readypos = GetPTPos(ePTLoc.READY);
			else if (axis == eAxis.PL_MOVE) readypos = GetLMPos(eLMLoc.READY);
			else if (axis == eAxis.PR_MOVE) readypos = GetRMPos(eRMLoc.READY);
			else if (axis == eAxis.PR_UPDN) readypos = GetRZPos(eRZLoc.READY);
			else if (axis == eAxis.PL_UPDN) readypos = GetLZPos(eLZLoc.READY);
			else return false;

			var offset = getPositionOffset(axis, readypos.Position);
			if (offset < allowoffset) return true;    //오차가 2미만이라면 안전하다
			return false;
		}

		private static Boolean Home_Validation(eAxis axis, out string errorMessage)
		{
			Boolean retval = true;
			//   int axis = (int)axis_;

			var CW = false; //홈검색은 항상 뒤로 움직인다.
			if (!Move_Validation(axis, CW, out errorMessage)) retval = false;    //이동이 불가한 경우 체크
			else if (axis == eAxis.PX_PICK)
			{
				//Z축 홈이 필요하다
				var zFHome = PUB.mot.IsHomeSet((int)eAxis.PZ_PICK);
				var zRHome = PUB.mot.IsHomeSet((int)eAxis.Z_THETA);
				if (zFHome == false || zRHome == false)
				{
					//errorMessage = "Z-PICKER,Z-THETA 축 홈이 필요 합니다";
					//retval = false;
				}
			}
			else if (axis == eAxis.PL_MOVE)   //Z축의 경우 Y축이 홈으로 된 상태여야 한다.
			{
				if (PUB.mot.IsHomeSet((int)eAxis.PL_UPDN) == false)
				{
					errorMessage = "PRINT-L Z axis home is required";
					retval = false;
				}
			}
			else if (axis == eAxis.PR_MOVE)   //Z축의 경우 Y축이 홈으로 된 상태여야 한다.
			{
				if (PUB.mot.IsHomeSet((int)eAxis.PR_UPDN) == false)
				{
					errorMessage = "PRINT-R Z axis home is required";
					retval = false;
				}
			}
			return retval;
		}

		//private static Boolean Move_Validation(eAxis axis, out string errorMessage)
		//{
		//	errorMessage = string.Empty;

		//	if (DIO.IsEmergencyOn() == true)
		//	{
		//		errorMessage = ("비상정지 상태일때에는 움직일 수 없습니다.");
		//		return false;
		//	}

		//	return true;
		//}
		private static Boolean Move_Validation(eAxis axis, Boolean MoveCW, out string errorMessage)
		{
			errorMessage = string.Empty;

			if (DIO.IsEmergencyOn() == true)
			{
				errorMessage = ("Cannot move when in emergency stop state.");
				return false;
			}

			//홈이 잡힌상태일때
			if (PUB.mot.IsHomeSet((short)axis))
			{
				if (axis == eAxis.PX_PICK)
				{
					var Pos = MOT.GetPXPos(ePXLoc.PICKON);
					var PosOffset = MOT.getPositionOffset(Pos);

					if(AR.SETTING.Data.Log_Debug)
					PUB.logDbg.Add($"X-axis jog center offset:{PosOffset:N3}");

					if (MoveCW==false && PosOffset < -1) //좌측위치로 이동하는 경우
					{
					   	if(PUB.mot.IsHomeSet((int)eAxis.PL_MOVE))
						{
							var PosY = MOT.GetLMPos(eLMLoc.READY);
							var PosYOffse = MOT.getPositionOffset(PosY);
							if(PosYOffse < -1)
							{
								//프린터 Y축이 더 안쪽으로 들어와있으니 충돌할 수 있다.
								errorMessage = "Possible collision with Print(L) axis";
								return false;
							}
						}
					}
					else if(MoveCW && PosOffset > 1)	//우측으로 이동하는 경우
					{
						if (PUB.mot.IsHomeSet((int)eAxis.PR_MOVE))
						{
							var PosY = MOT.GetRMPos(eRMLoc.READY);
							var PosYOffse = MOT.getPositionOffset(PosY);
							if (PosYOffse < -1)
							{
								//프린터 Y축이 더 안쪽으로 들어와있으니 충돌할 수 있다.
								errorMessage = "Possible collision with Print(R) axis";
								return false;
							}
						}
					}
				}
			}

			return true;
		}

        #region "Common Util"

        /// <summary>
        /// 지정한 축의 모델정보에서 해당 위치값을 찾아 반환 합니다
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="pos"></param>
        /// <returns></returns>
        private static sPositionData GetAxPos(eAxis axis, int pos)
        {
            return GetAxpos((short)axis, pos);
        }
        private static sPositionData GetAxpos(short axis, int pos)
        {
            var retval = new sPositionData();
            retval.Clear();
            if (PUB.Result.mModel == null || PUB.Result.mModel.isSet == false)
            {
                retval.Message = "Motion model is not set";
                return retval;
            }

            if(axis >= PUB.Result.mModel.Position.Count)
            {
                retval.Message = $"Motion model (axis) information not found ({axis}/{PUB.Result.mModel.Position.Count})";
                return retval;
            }

            if (pos >= PUB.Result.mModel.Position[axis].Length)
            {
                retval.Message = $"Motion model (position) information not found ({pos}/{PUB.Result.mModel.Position[axis].Length})";
                return retval;
            }

            var data = PUB.Result.mModel.Position[axis][pos];
            if (data.index == -1)
            {
                retval.Message = string.Format("Value for axis:{0} position:{1} does not exist", axis, pos);
                return retval;
            }
            retval.Axis = axis; //220301
            retval.Position = data.value;
            retval.Speed = data.speed;
            retval.Acc = data.acc;


            //환경설정에서 저속모드로 설정했다면 지정된 속도로만 처리한다
            if (AR.SETTING.Data.Enable_SpeedLimit == true)
                retval.Speed = Math.Min(retval.Speed, AR.SETTING.Data.LimitSpeed);

            ////시스템설정의 속도 체크 220524
            //var maxspeed = SETTING.System.GetMaxSpeed;
            //var motidx = (int)axis;
            //if (motidx >= 0 && motidx < maxspeed.Length && maxspeed[motidx] > 0)
            //{
            //    retval.Speed = Math.Min(retval.Speed, maxspeed[motidx]);
            //}

            ////시스템설정의 가속도체크
            //var maxAcc = SETTING.System.GetMaxAcc;
            //if (motidx >= 0 && motidx < maxAcc.Length && maxAcc[motidx] > 0)
            //{
            //    retval.Acc = Math.Min(retval.Acc, maxAcc[motidx]);
            //}

            if (data.dcc < 1) retval.Dcc = retval.Acc;
            else retval.Dcc = data.dcc;

            //retval.isError = false;
            retval.Message = string.Empty;
            return retval;
        }

        /// <summary>
        /// 지정축의 모션을 중지 합니다.
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="reason"></param>
        /// <param name="estop"></param>
        public static void Stop(eAxis axis, string reason, Boolean estop = false)
        {
            if (PUB.mot.IsInit == false) return;
            if (PUB.mot.IsServAlarm((short)axis)) return;
            PUB.mot.MoveStop(reason, (short)axis, estop);

        }

        //지정한 옵셋범위에 들어있는지 체크합니다.
        static Boolean MatchPosition(double cpos, double tpos, double diff = 0.1)
        {
            var offset = Math.Abs(tpos - cpos);
            return offset <= diff;
        }
        public static double getPositionOffset(eAxis axis, double cmdPos)
        {
            return getPositionOffset((short)axis, cmdPos);
        }

        /// <summary>
        /// 현재위치에서 지정된 위치를 뺀 값입니다. +가 반환되면 현재 위치가 지정위치보다 멀리 있다는 뜻입니다.
        /// </summary>
        /// <param name="Pos"></param>
        /// <returns></returns>
        public static double getPositionOffset(sPositionData Pos)
        {
            return getPositionOffset(Pos.Axis, Pos.Position);
        }
        /// <summary>
        /// 현재위치에서 지정된 위치를 뺀 값입니다. +가 반환되면 현재 위치가 지정위치보다 멀리 있다는 뜻입니다.
        /// </summary>
        /// <param name="axis"></param>
        /// <param name="cmdPos"></param>
        /// <returns></returns>
        public static double getPositionOffset(short axis, double cmdPos)
        {
            var coffset = (PUB.mot.GetActPos(axis) - cmdPos);
            return coffset;// return coffset < offset;
        }

        public static bool getPositionMatch(eAxis axis, double cmdPos, double offset = 0.1)
        {
            return getPositionMatch((short)axis, cmdPos, offset);
        }

        public static bool getPositionMatch(sPositionData pos, double offset = 0.1)
        {
            return getPositionMatch(pos.Axis, pos.Position, offset);
        }


        public static bool getPositionMatch(short axis, double cmdPos, double offset = 0.1)
        {
            var actpos = PUB.mot.GetActPos(axis);
            var coffset = Math.Abs(actpos - cmdPos);
            return coffset <= offset;
        }

        public static List<string> GetActiveLockList(eAxis axis, CInterLock Lck)
        {
            var locklist = new List<string>();

            if (Lck.IsEmpty() == false)
            {
                for (int i = 0; i < Lck.Length; i++)
                {
                    if (Lck.get(i))
                    {
                        var vStr = $"[{i}]";
                        vStr = ((eILock)i).ToString();
                        locklist.Add(vStr);
                    }
                }
            }
            return locklist;
        }

        public static Boolean Home(string reason, eAxis axis_, Boolean useValidcheck = true)
        {
            string Message;
            int axis = (int)axis_;
            if (useValidcheck == true && !Home_Validation(axis_, out Message))
            {
                PUB.mot.RaiseMessage(string.Format("[{0}-Axis] Move Error : {1}", axis, Message), true);
                return false;
            }

            var HomespdH = PUB.system_mot.GetHomeSpeedHigh;
            var HomespdL = PUB.system_mot.GetHomeSpeedLow;
            var homespdA = PUB.system_mot.GetHomeSpeedAcc;
            var useOrgSensor = PUB.system_mot.GetUseOrigin;

            //Pub.mot.Home(1, arDev.AzinAxt.eMotionDirection.Negative, arDev.AzinAxt.eSoftLimitStopMode.SStop, COMM.SETTING.Data.HZSpeed, COMM.SETTING.Data.HZAcc);
            //Boolean useOrgSensor = !disable_org[axis]; // (axis_ != eAxis.Marking); //A센서는 ORG가 없으므로 NE센서만으로 처리해야 함
            return PUB.mot.Home(reason, (short)axis, MOTION_DIRECTION.Negative,
                STOPTYPE.EStop,
                HomespdH[axis],
                HomespdL[axis],
                homespdA[axis],
                useOrgSensor[axis]);
        }


        private static DateTime[] MotCmdtime = new DateTime[] { DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now };
        private static double[] MotCmdPos = new double[] { 0, 0, 0, 0, 0, 0, 0, 0 };

        public static Boolean Move(eAxis axis, sPositionData posdata)
        {
            return Move(axis, posdata.Position, posdata.Speed, posdata.Acc);
        }

        public static Boolean Move(sPositionData posdata)
        {
            return Move((eAxis)posdata.Axis, posdata);
        }

        public static Boolean Move(eAxis axis, double pos_mm, double speed, double acc = 1000, Boolean relative = false, Boolean validchk = true, Boolean UserInterLock = true)
        {

            //너무빠른시간 동작하지 못하게 한다
            if (MotCmdPos[(int)axis] == pos_mm)
            {
                var ts = DateTime.Now - MotCmdtime[(int)axis];
                if (ts.TotalMilliseconds < 1000)
                {
                    //너무 빠르게 재시도하지 않게 한다 210115
                    Console.WriteLine("mot command skip : " + axis.ToString() + "pos:" + pos_mm.ToString() + " too short");
                    MotCmdtime[(int)axis] = DateTime.Now.AddMilliseconds(-1500);
                    return true;
                }
                else
                {
                    MotCmdtime[(int)axis] = DateTime.Now;
                    MotCmdPos[(int)axis] = pos_mm;
                }
            }
            else
            {
                MotCmdtime[(int)axis] = DateTime.Now;
                MotCmdPos[(int)axis] = pos_mm;
            }

            //이동유효성검사
            string Message;
            if (validchk == true)
            {
                //현재위치보다 작으면 ccw
                var isCW = pos_mm >= PUB.mot.GetActPos((short)axis);
                if (!Move_Validation(axis, isCW, out Message))
                {
                    PUB.log.AddE(string.Format("[{0}-Axis] Move Error : {1}", axis, Message));
                    //Pub.mot.RaiseMessage(, true);
                    return false;
                }
            }

            //해당 축 ILOCK체크
            if (UserInterLock)
            {
                //lock이 걸린경우 lock 걸린 항목을 모두 확인하면 좋다
                var ilock = PUB.iLock[(int)axis];
                if (ilock.IsEmpty() == false)
                {
                    var locklist = MOT.GetActiveLockList(axis, ilock);
                    PUB.mot.ErrorMessage = $"{ilock.Tag} Interlock(" + string.Join(",", locklist) + ")";
                    //PUB.Result.SetResultMessage(eResult.MOTION, eECode.INTERLOCK, eNextStep.PAUSE, PUB.mot.ErrorMessage);
                    return false;
                }
            }

            var pos_pulse = pos_mm;// *10;
            var result = PUB.mot.Move((short)axis, pos_pulse, speed, acc, acc, relative, false, validchk);
            if (!result) PUB.mot.RaiseMessage("Move(X) Error : " + PUB.mot.ErrorMessage, true);
            return result;
        }


        public static Boolean JOG(short _Axis, MOTION_DIRECTION Dir, double _Vel, double _Acc, double _Dec, Boolean sCurve = false, Boolean EnableValidCheck = true, Boolean UserInterLock = true)
        {
            eAxis axis = (eAxis)_Axis;

            //웨이퍼가 감지되는 상태일때 OPEN 되어있다면 이동하지 못하게 한다
            if (EnableValidCheck == true)
            {
                var curpost = PUB.mot.GetActPos(_Axis);
                var IsMoveCW = false;

                IsMoveCW = Dir == MOTION_DIRECTION.Positive;

                if (!Move_Validation(axis, IsMoveCW, out string Message))
                {
                    PUB.log.AddE(string.Format("[{0}-Axis] JOG Error : {1}", axis, Message));
                    return false;
                }
            }
            else PUB.log.AddAT($"Validation check disabled during jog movement (axis:{axis})");

            //해당 축 ILOCK체크
            if (UserInterLock)
            {
                //lock이 걸린경우 lock 걸린 항목을 모두 확인하면 좋다
                if (PUB.iLock[_Axis].IsEmpty() == false)
                {
                    var locklist = MOT.GetActiveLockList(axis, PUB.iLock[_Axis]);
                    PUB.mot.ErrorMessage = $"{axis} Interlock(" + string.Join(",", locklist) + ")";
                    return false;
                }
            }
            else PUB.log.AddAT($"Interlock check disabled during jog movement (axis:{axis})");


            return PUB.mot.JOG(_Axis, Dir, _Vel, _Acc, sCurve, EnableValidCheck);
        }




        #endregion
    }
}
