using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Timers;
using MECF.Framework.Simulator.Core.SubstrateTrackings;

namespace MECF.Framework.Simulator.Core.Robots
{

    public class BrooksMag7RobotSimulator : RobotSimulator
    {
        protected Random _rd = new Random();
        public bool Failed { get; set; }
        public string ErrorCode { get; set; }

        public bool EventChecked { get; set; }
        public string EventCode { get; set; }

        public override Dictionary<string, double> MoveTimes
        {
            get { return moveTimes; }
            set { moveTimes = value; }
        }

        public override ReadOnlyCollection<string> Arms
        {
            get { return arms; }
        }

        //private static string source = "BrooksMag7";
        private static string msgDone = "_RDY";
        private static string msgError = "_ERR";
        private readonly string armAPan1 = "VTM.ArmA.Left";
        private readonly string armAPan2 = "VTM.ArmA.Right";
        private readonly string armBPan1 = "VTM.ArmB.Left";
        private readonly string armBPan2 = "VTM.ArmB.Right";
        private System.Timers.Timer timer;
        private string currentStation;
        private string newLocation;
        private string currentArm;
        private string newArm = "";
        private string lastMsg;
        private Dictionary<string, double> moveTimes;
        private ReadOnlyCollection<string> arms;


        public BrooksMag7RobotSimulator() : base(1102, 0, "\r", ' ', 5)
        {

            List<string> armsList = new List<string>();
            armsList.Add(armAPan1);
            armsList.Add(armAPan2);
            armsList.Add(armBPan1);
            armsList.Add(armBPan2);
            arms = new ReadOnlyCollection<string>(armsList);

            // create the message handling dictionary
            AddCommandHandler("HOME", HandleHome);
            AddCommandHandler("PICK", HandlePick);
            AddCommandHandler("PLACE", HandlePlace);
            AddCommandHandler("GOTO", HandleGoto);
            AddCommandHandler("SWAP", HandleExchange);
            AddCommandHandler("RQ", HandleRequest);
            AddCommandHandler("Unknown", HandleUnknown);

            //AddCommandHandler("SVON", HandleSVON);
            // AddCommandHandler("SVOFF", HandleSVOFF);


            timer = new System.Timers.Timer();
            timer.Enabled = false;
            timer.AutoReset = false;
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);

            // Default move times based on Brooks spreadsheet
            moveTimes = new Dictionary<string, double>();
            moveTimes["PickPlace"] = 2.8;
            moveTimes["Move90Degrees"] = 1.69;
            moveTimes["Move180Degrees"] = 2.11;
            moveTimes["HeightAdjust"] = 0.9;
            moveTimes["ExtendRetract"] = 1.3;
            moveTimes["WaferFactor"] = 1.0;
            moveTimes["SwapAtPM"] = 7.3;

            // Original default times based on Brooks log files
            //moveTimes["PickPlace"] = 3.2;
            //moveTimes["Move90Degrees"] = 1.5;
            //moveTimes["Move180Degrees"] = 1.9;
            //moveTimes["HeightAdjust"] = 0.9;
            //moveTimes["ExtendRetract"] = 1.2;
            //moveTimes["WaferFactor"] = 1.15;

            currentArm = "A";

            currentStation = "Unknown";


        }

        internal void HandleSVON(string msg)
        {

            if (ErrorMessage == "SVON Failed")
            {
                HandleError(ErrorMessage);
                return;
            }

            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 1)
            {

                HandleError("Invalid SVON command (arguments)");
                return;
            }
            HandleMove(RobotStateEnum.SVON, cmdComponents);

        }

        internal void HandleSVOFF(string msg)
        {

            if (ErrorMessage == "SVOFF Failed")
            {
                HandleError(ErrorMessage);
                return;
            }

            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 1)
            {

                HandleError("Invalid SVOFF command (arguments)");
                return;
            }
            HandleMove(RobotStateEnum.SVOFF, cmdComponents);

        }

        internal void HandleHome(string msg)
        {

            if (ErrorMessage == "Home Failed")
            {
                HandleError(ErrorMessage);
                return;
            }

            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 2)
            {

                HandleError("Invalid homing command (arguments)");
                return;
            }
            HandleMove(RobotStateEnum.Homing, cmdComponents);

        }


        internal void HandlePick(string msg)
        {

            msg = msg.Trim();
            if (ErrorMessage == "Pick Failed")
            {
                HandleError(ErrorMessage);
                return;
            }

            if (Failed && !string.IsNullOrEmpty(ErrorCode))
            {
                HandleError(ErrorMessage);
                return;
            }
            if (EventChecked && !string.IsNullOrEmpty(EventCode))
            {
                HandleEvent(EventCode);
                return;
            }
            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 6 && cmdComponents.Length != 8 && cmdComponents.Length != 10)
            {


                HandleError("Invalid pick command (arguments)");
                return;
            }

            RobotStateEnum rs = RobotStateEnum.Picking;
            if (cmdComponents.Length > 6 && cmdComponents[6] == "ENRT")
                rs = RobotStateEnum.Extending;
            else if (cmdComponents.Length > 6 && cmdComponents[6] == "STRT")
                rs = RobotStateEnum.Retracting;

            lastMsg = msg;

            HandleMove(rs, cmdComponents);

        }

        internal void HandlePlace(string msg)
        {

            msg = msg.Trim();
            if (ErrorMessage == "Place Failed")
            {
                HandleError(ErrorMessage);
                return;
            }


            if (Failed && !string.IsNullOrEmpty(ErrorCode))
            {
                HandleError(ErrorMessage);
                return;
            }
            if (EventChecked && !string.IsNullOrEmpty(EventCode))
            {
                HandleEvent(EventCode);
                return;
            }
            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 6 && cmdComponents.Length != 8 && cmdComponents.Length != 10)
            {

                HandleError("Invalid place command (arguments)");
                return;
            }

            RobotStateEnum rs = RobotStateEnum.Placing;
            if (cmdComponents.Length > 6 && cmdComponents[6] == "ENRT")
                rs = RobotStateEnum.Extending;
            else if (cmdComponents.Length > 6 && cmdComponents[6] == "STRT")
                rs = RobotStateEnum.Retracting;

            lastMsg = msg;

            HandleMove(rs, cmdComponents);

        }

        internal void HandleExchange(string msg)
        {

            if (ErrorMessage == "Swap Failed" || ErrorMessage == "Place Failed" || ErrorMessage == "Pick Failed")
            {
                HandleError(ErrorMessage);
                return;
            }

            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 4)
            {

                HandleError("Invalid swap command (arguments)");
                return;
            }
            lastMsg = msg;


            HandleMove(RobotStateEnum.Exchanging, cmdComponents);

        }


        internal void HandleRequest(string msg)
        {
            string[] components = msg.Split(_msgDelimiter);
            string reply = components[1];
            if (components[1] == "WAFER" && components[2] == "PRESENT")
                reply += " " + components[2] + GetArmStates();
            else if (components[1] == "ERRMSG")
                reply = LookupError(components[2]);


            OnWriteMessage(reply);

            OnWriteMessage(msgDone);
        }


        internal void HandleUnknown(string msg)
        {

            OnWriteMessage(msgDone);
        }


        internal void HandleGoto(string msg)
        {

            string[] cmdComponents = msg.Split(_msgDelimiter);
            if (cmdComponents.Length != 11)
            {

                HandleError("Invalid move command (arguments)");
                return;
            }
            HandleMove(RobotStateEnum.Approaching, cmdComponents);

        }

        private bool HandleMove(RobotStateEnum action, string[] cmdComponents)
        {

            if (robotStateArgs.State != RobotStateEnum.Idle &&
                (action == RobotStateEnum.Homing && robotStateArgs.State != RobotStateEnum.Errored))  // allow homes when in error, but not other moves
            {

                HandleError("Already moving");
                return false;
            }

            newLocation = "Unknown";
            switch (action)
            {
                case RobotStateEnum.Picking:
                case RobotStateEnum.Placing:
                case RobotStateEnum.Approaching:
                case RobotStateEnum.Extending:
                case RobotStateEnum.Retracting:
                case RobotStateEnum.Exchanging:
                case RobotStateEnum.SVON:
                case RobotStateEnum.SVOFF:
                    break;

                default:

                    break;
            }


            if (ErrorMessage != "Pick Failed" && ErrorMessage != "Place Failed" && ErrorMessage != "Home Failed" &&
                ErrorMessage != "Swap Failed" && !string.IsNullOrEmpty(ErrorMessage))
            {
                HandleError(ErrorMessage);
                return false;
            }

            double delay = GetMoveTime(action);


            timer.Interval = delay * 1000;
            timer.Enabled = true;


            return true;

        }


        private double GetMoveTime(RobotStateEnum action)
        {


            double rotationTime = 0;
            double zMoveTime = 0;
            if (newLocation != currentStation)
            {
                if (currentStation == "LL1" || currentStation == "LL2")
                    rotationTime = newLocation == "PM2" ? moveTimes["Move180Degrees"] : moveTimes["Move90Degrees"];
                else
                    rotationTime = currentStation == "PM2" ? moveTimes["Move180Degrees"] : moveTimes["Move90Degrees"];
                double factor = 1.0;
                if (WaferTrack.Instance.IsOccupied(armAPan1) || WaferTrack.Instance.IsOccupied(armAPan2) ||
                    WaferTrack.Instance.IsOccupied(armBPan1) || WaferTrack.Instance.IsOccupied(armBPan2))
                {
                    factor = moveTimes["WaferFactor"];
                }

                rotationTime *= factor;
            }
            else if (newArm != currentArm)
                zMoveTime = moveTimes["HeightAdjust"];

            switch (action)
            {
                case RobotStateEnum.Approaching:
                    if (newLocation == currentStation)
                        return moveTimes["HeightAdjust"];

                    return rotationTime;

                case RobotStateEnum.Extending:
                case RobotStateEnum.Retracting:
                    return moveTimes["ExtendRetract"] + rotationTime + zMoveTime;

                case RobotStateEnum.Picking:
                    return moveTimes["PickPlace"] + zMoveTime;

                case RobotStateEnum.Placing:
                    if (newLocation.StartsWith("PM"))
                        return moveTimes["PickPlace"] + zMoveTime;
                    else
                        return moveTimes["ExtendRetract"] + rotationTime + zMoveTime;

                case RobotStateEnum.Exchanging:
                    return moveTimes["SwapAtPM"];

                default:
                    return moveTimes["Move90Degrees"];
            }
        }


        private void HandleError(string msg)
        {
            string errorCode = string.Format("0x{0}", lastError.ToString("x8"));
            lastError++;
            errorLookup[errorCode] = msg;

            OnWriteMessage(msgError + " " + ErrorCode);

            OnWriteMessage(msgDone);
        }
        private void HandleEvent(string msg)
        {
           // string errorCode = string.Format("0x{0}", lastError.ToString("x8"));
            //lastError++;
            //errorLookup[errorCode] = msg;

            OnWriteMessage(msg);

            OnWriteMessage(msgDone);
        }

        private string GetArmStates()
        {
            return " N N";


        }

        private void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            currentStation = newLocation;
            currentArm = newArm;

            if (robotStateArgs.State == RobotStateEnum.Picking || robotStateArgs.State == RobotStateEnum.Placing ||
                      robotStateArgs.State == RobotStateEnum.Extending || robotStateArgs.State == RobotStateEnum.Exchanging)
            {
                lastMsg = "";
            }
            SetRobotState(RobotStateEnum.Idle);
            timer.Enabled = false;
            OnWriteMessage(msgDone);

        }



    }
}
