using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using parkMonitor.entity;
using parkMonitor.model;
using parkMonitor.server;
using System.Configuration;
using parkMonitor.tools;
using parkMonitor.server.uiLogServer;
using parkMonitor.LOG;
using parkMonitor.Database2;

namespace parkMonitor.server.CoreThread
{
    /// <summary>
    /// 队列线程，处理号牌与停取车指令业务逻辑
    /// </summary>
    public class QueuingThread : IEquipments
    {
        /// <summary>
        /// 预约停车队列
        /// </summary>
        Queue<Command> BookParkingQueue = new Queue<Command>();
        /// <summary>
        /// 号牌队列，放入来自于号牌机的号牌。与当前指令队列中指令匹配不上的号牌将被放入该队列
        /// </summary>
        Queue<NumberMachineNode> LicenseQueue = new Queue<NumberMachineNode>();
        /// <summary>
        /// 停车指令号牌，放入来自于web或手动的停车指令。与当前号牌队列中号牌匹配不上的号牌将被放入该队列
        /// </summary>
        Queue<Command> StoreCmdQueue = new Queue<Command>();
        /// <summary>
        /// 合法停车指令队列，放入已匹配上的指令。
        /// </summary>
        Queue<Command> ValidStoreCmdQueue = new Queue<Command>();
        /// <summary>
        /// 取车指令队列，放入来自于web或手动的取车指令。
        /// </summary>
        Queue<Command> FetchCmdQueue = new Queue<Command>();
        /// <summary>
        /// 可执行阻塞队列，放入来自于取指或合法停指的经过调度的停取车指令。
        /// </summary>
        BlockingQueue ExecutableCmdQueue = new BlockingQueue();
        int fetchStoreRatio, licenseTime, userTime, bookTime;
        bool isClosing = false;
        IEquipments webHandle = null;
        IEquipments numMachineHandle = null;
        /// <summary>
        /// 号牌暂存对象
        /// </summary>
        NumberMachineNode license;
        /// <summary>
        /// web消息暂存变量
        /// </summary>
        MessageUTF8 webMsg;

        /// <summary>
        /// 队列构造函数
        /// </summary>
        public QueuingThread()
        {
            webHandle = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.Web);
            numMachineHandle = EquipmentSimpleFactory.ins.FindEquipment(EquipmentName.NumMachine);
            try
            {
                fetchStoreRatio = Int32.Parse(ConfigurationManager.AppSettings.Get("fetch_store_ratio"));
                licenseTime = Int32.Parse(ConfigurationManager.AppSettings.Get("licenseTime"));
                userTime = Int32.Parse(ConfigurationManager.AppSettings.Get("userTime"));
                bookTime = Int32.Parse(ConfigurationManager.AppSettings.Get("bookTime"));
            }
            catch (Exception) { fetchStoreRatio = 3; UILogServer.ins.error("未找到停取车比例与计时参数"); Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "未找到停取车比例与计时参数"); }
        }

        /// <summary>
        /// 从web线程获得用户指令
        /// </summary>
        private void ReceiveFromWebThread()
        {
            while (!isClosing)
            {
                Command cmd = null;
                //接收web消息
                if (webHandle != null)
                {
                    try
                    {
                        webMsg = (MessageUTF8)(((MessageUTF8)webHandle.GetMessage()).Clone());
                    }
                    catch (Exception) { UILogServer.ins.error("用户指令异常"); Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "用户指令异常"); }
                }
                //get useful data from webMsg and assign it to output data structure
                if (webMsg != null)
                {
                    cmd = new Command();
                    cmd.commandType = webMsg.cmd;
                    cmd.LicenseNum = webMsg.context;
                    cmd.userID = webMsg.sender;
                    cmd.garageID = webMsg.garageID;
                    cmd.parkingRecordsID = webMsg.parkingRecordsID;
                    if (cmd.commandType == 'p')
                    {
                        DateTime dt = new DateTime();
                        if (webMsg.bookTime == null)
                        {
                            cmd.TimeRecord = "";
                        }
                        else if (DateTime.TryParse(webMsg.bookTime, out dt))
                        {
                            cmd.TimeRecord = webMsg.bookTime;
                        }
                        else { Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "web发送预约时间异常，指令丢弃"); return; }
                    }
                    else
                    {
                        cmd.TimeRecord = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    }
                    webMsg = null;
                }
                //input user command
                if (cmd != null)
                {
                    if (cmd.commandType.Equals('f'))
                    {
                        lock (FetchCmdQueue)
                        {
                            FetchCmdQueue.Enqueue((Command)cmd.Clone());
                        }
                        UILogServer.ins.info("收到web取车指令：" + cmd.LicenseNum);
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到web取车指令：" + cmd.LicenseNum);
                    }
                    //停车先遍历
                    else if (cmd.commandType.Equals('s'))
                    {
                        UserCmdMatch(cmd);
                        UILogServer.ins.info("收到web停车指令：" + cmd.LicenseNum);
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到web停车指令：" + cmd.LicenseNum);
                    }
                    else if (cmd.commandType.Equals('p'))
                    {
                        if (cmd.TimeRecord == "")
                        {
                            //取消预约
                            UILogServer.ins.info("收到web预约停车取消指令：" + cmd.LicenseNum);
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到web预约停车取消指令：" + cmd.LicenseNum);
                            ParkingSpaceManager.ins.CancleBooking(cmd.LicenseNum);
                        }
                        else
                        {
                            //正常预约
                            UILogServer.ins.info("收到web预约停车指令：" + cmd.LicenseNum);
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到web预约停车指令：" + cmd.LicenseNum);
                            int count = 0;
                            if (!BookParkingQueue.Contains(cmd))
                            {
                                ParkingSpaceManager.ins.MallocParkingSpace(new CEntrance(EntityForCore.ins.parkingEntX, EntityForCore.ins.parkingEntY, EntityForCore.ins.parkingEntZ), cmd, true, out count);
                                BookParkingQueue.Enqueue(cmd);
                            }
                        }
                    }else if (cmd.commandType.Equals('0'))
                    {
                        UILogServer.ins.info("收到web心跳");
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到web心跳");
                    }
                    else
                    {
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.WARNING, "从web收到无法识别的用户指令");
                        UILogServer.ins.warn("从web收到无法识别的用户指令,并非停取车");
                    }
                }
                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// 队列线程主业务逻辑
        /// </summary>
        private void Run()
        {
            NumberMachineNode temp = null;
            NumberMachineMessage numMsg = null;
            while (!isClosing)
            {
                lock (LicenseQueue)
                {
                    //本地号牌已使用，则接收新的号牌消息
                    if (numMachineHandle != null && !NumMachineNodeValidation(license))
                    {
                        while (numMsg == null)
                        {
                            numMsg = ((NumberMachineMessage)numMachineHandle.GetMessage());
                        }
                        license = numMsg.aNode;
                        //无重复则匹配指令，匹配失败则入号牌队列，并将号牌机线程获得的Node中ip标记为已使用
                        if (NumMachineNodeValidation(license))
                        {
                            if (!LicenseQueue.Contains(license))
                            {
                                UILogServer.ins.info("已扫描到号牌：" + license.LicenseNum + "，匹配中");
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "已扫描到号牌：" + license.LicenseNum + "，匹配中");
                                //先匹配指令
                                NumMatch(ref license);
                            }
                            else
                            {
                                license.ip = "used";
                            }
                        }
                    }
                    //将已被使用的号牌从号牌队列排除
                    for (int i = 0; i < LicenseQueue.Count; i++)
                    {
                        temp = LicenseQueue.Dequeue();
                        if (temp != null && temp.ip != "used")
                        {
                            LicenseQueue.Enqueue(temp);
                        }
                    }
                }

                //队列业务逻辑
                //UserCmdMatch();
                Scheduling();
                lock (StoreCmdQueue)
                {
                    UserCmdTimeOut();
                }
                lock (LicenseQueue)
                {
                    NumTimeOut();
                }
                lock (BookParkingQueue)
                {
                    BookParkingTimeOut();
                }
                Thread.Sleep(500);
            }
        }

        /// <summary>
        /// 号牌节点有效性验证
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool NumMachineNodeValidation(NumberMachineNode node)
        {
            return (node != null && node.ip != null && node.ip != "" && node.ip != "used") ? true : false;
        }

        private void BookParkingTimeOut()
        {
            for (int i = 0; i < BookParkingQueue.Count; i++)
            {
                Command cmd = BookParkingQueue.Dequeue();
                if (cmd != null)
                {
                    TimeSpan ts = DateTime.Now - DateTime.Parse(cmd.TimeRecord);
                    if (ts.TotalMinutes >= bookTime)
                    {
                        //超时则释放车位，丢出指令
                        UILogServer.ins.log("预约指令 " + cmd.LicenseNum + " 已存在超过" + bookTime + "分钟 ,强制出队");
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "预约指令 " + cmd.LicenseNum + " 已存在超过" + bookTime + "分钟 ,强制出队");
                        ParkingSpaceManager.ins.CancleBooking(cmd.LicenseNum);
                    }
                    else
                    {
                        BookParkingQueue.Enqueue(cmd);
                    }
                }
            }
        }

        /// <summary>
        /// 停车指令超时处理
        /// </summary>
        private void UserCmdTimeOut()
        {
            Command cmd = null;
            for (int i = 0; i < StoreCmdQueue.Count; i++)
            {
                cmd = StoreCmdQueue.Dequeue();

                //指令超时处理
                TimeSpan userTimeSpan = DateTime.Now - DateTime.Parse(cmd.TimeRecord);
                //Console.WriteLine("指令已扫描时间：" + userTimeSpan.TotalSeconds);
                if (userTimeSpan.TotalMinutes >= userTime)
                {
                    UILogServer.ins.log("用户" + cmd.userID + "指令 " + cmd.LicenseNum + " 已存在超过" + String.Format("{0:F1}", userTimeSpan.TotalMinutes) + "分钟 ,强制出队");
                    Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "用户指令 " + cmd.LicenseNum + " 已存在超过" + String.Format("{0:F1}", userTimeSpan.TotalMinutes) + "分钟 ,强制出队");
                    //cmd.LicenseNum = "";
                    cmd.commandType = 'e';
                    lock (ValidStoreCmdQueue)
                    {
                        ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
                    }
                    if (StoreCmdQueue.Count != 0)
                        cmd = StoreCmdQueue.Dequeue();
                    else
                        break;
                }
                //未超时则归队
                else
                {
                    StoreCmdQueue.Enqueue((Command)cmd.Clone());
                }
            }
        }

        /// <summary>
        /// 号牌超时处理
        /// </summary>
        private void NumTimeOut()
        {
            for (int i = 0; i < LicenseQueue.Count; i++)
            {
                NumberMachineNode node = LicenseQueue.Dequeue();
                if (NumMachineNodeValidation(node))
                {
                    //计算时间差，超过时间输出提示，并将该Node标记为已使用
                    TimeSpan licTimeSpan = DateTime.Now - DateTime.Parse(node.TimeRecord);
                    if (licTimeSpan.TotalMinutes >= licenseTime)
                    {
                        //异常号牌不再入队，而是界面提示管理员,并让号牌机线程
                        UILogServer.ins.log("号牌 " + node.LicenseNum + " 已扫描超过" + String.Format("{0:F1}", licTimeSpan.TotalMinutes) + "分钟 ,强制出队");
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.ERROR, "号牌 " + node.LicenseNum + " 已扫描超过" + String.Format("{0:F1}", licTimeSpan.TotalMinutes) + "分钟 ,强制出队");
                        //停车完成，将相应车牌复位
                        NumberMachineMessage numberMachineMessage = new NumberMachineMessage();
                        numberMachineMessage.aNode = new NumberMachineNode();
                        numberMachineMessage.aNode.ip = "";
                        numberMachineMessage.aNode.LicenseNum = node.LicenseNum;
                        numMachineHandle.SetMessage(numberMachineMessage);
                        node.ip = "used";
                    }
                }
                LicenseQueue.Enqueue(node);
            }
        }

        /// <summary>
        /// 单用户指令匹配号牌队列
        /// </summary>
        private void UserCmdMatch(Command cmd)
        {
            lock (StoreCmdQueue)
            {
                NumberMachineNode num = null;
                bool matched = false;
                num = new NumberMachineNode();
                lock (LicenseQueue)
                {
                    for (int i = 0; i < LicenseQueue.Count; i++)
                    {
                        num = LicenseQueue.Dequeue();
                        //匹配成功
                        if (NumMachineNodeValidation(num) && cmd.LicenseNum.Equals(num.LicenseNum))
                        {
                            cmd.TimeRecord = num.TimeRecord;
                            cmd.ip = num.ip;
                            cmd.id = num.id;
                            ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
                            matched = true;
                            break;
                        }
                        else
                        {
                            LicenseQueue.Enqueue((NumberMachineNode)num.Clone());
                        }
                    }
                }
                if (!matched && !StoreCmdQueue.Contains(cmd))
                {
                    StoreCmdQueue.Enqueue((Command)cmd.Clone());
                }
            }
        }

        /// <summary>
        /// 单号牌匹配用户指令队列
        /// </summary>
        private void NumMatch(ref NumberMachineNode license)
        {
            Command cmd = new Command();
            bool matched = false;
            lock (StoreCmdQueue)
            {
                for (int i = 0; NumMachineNodeValidation(license) && i < StoreCmdQueue.Count; i++)
                {
                    cmd = StoreCmdQueue.Dequeue();
                    //匹配成功
                    if (license.LicenseNum.Equals(cmd.LicenseNum))
                    {
                        cmd.TimeRecord = license.TimeRecord;
                        cmd.ip = license.ip;
                        cmd.id = license.id;
                        ValidStoreCmdQueue.Enqueue((Command)cmd.Clone());
                        matched = true;
                        break;
                    }
                    else
                    {
                        StoreCmdQueue.Enqueue((Command)cmd.Clone());
                    }
                }
            }
            lock (LicenseQueue)
            {
                if (!matched && !LicenseQueue.Contains(license))
                {
                    LicenseQueue.Enqueue((NumberMachineNode)license.Clone());
                }
            }
            license.ip = "used";
        }

        /// <summary>
        /// 停取车调度，控制停取车比例，可多取一停，也可一取一停
        /// </summary>
        private void Scheduling()
        {
            //scheduling algorithm
            int fetch = fetchStoreRatio;
            Command cmd = null;
            lock (FetchCmdQueue)
            {
                while (FetchCmdQueue.Count != 0 && fetch-- > 0)
                {
                    cmd = FetchCmdQueue.Dequeue();
                    if ((cmd.manual && ConnectionPoolManager.malfunctionLocal) || (!cmd.manual && ConnectionPoolManager.malfunctionRemote))
                    {
                        FetchCmdQueue.Enqueue(cmd);
                    }
                    else
                    {
                        UILogServer.ins.info("任务：取出号牌为 " + cmd.LicenseNum + " 的车辆，开始排队");
                        Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "取车命令：" + cmd.LicenseNum + "已进入可执行队列");
                        ExecutableCmdQueue.Enqueue((Command)cmd.Clone());
                    }
                }
            }
            lock (ValidStoreCmdQueue)
            {
                if (ValidStoreCmdQueue.Count != 0)
                {
                    cmd = ValidStoreCmdQueue.Dequeue();
                    if ((cmd.manual && ConnectionPoolManager.malfunctionLocal) || (!cmd.manual && ConnectionPoolManager.malfunctionRemote))
                    {
                        ValidStoreCmdQueue.Enqueue(cmd);
                    }
                    else
                    {
                        if (cmd.commandType.Equals('s'))
                        {
                            UILogServer.ins.info("任务：存入号牌为 " + cmd.LicenseNum + " 的车辆，开始排队");
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "停车命令：" + cmd.LicenseNum + "已进入可执行队列");
                        }
                        else if(cmd.commandType.Equals('e'))
                        {
                            UILogServer.ins.info("任务：超时指令处理 " + cmd.LicenseNum + " ，开始排队");
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "停车指令超时：" + cmd.LicenseNum);
                        }
                        ExecutableCmdQueue.Enqueue((Command)cmd.Clone());
                    }
                }
            }
        }

        private string QueueDisplay(Queue<Command> queue)
        {
            string temp = "[";
            if (queue != null && queue.Count != 0)
            {
                Command cmd = null;
                for (int i = 0; i < queue.Count; i++)
                {
                    cmd = queue.Dequeue();
                    temp += cmd.LicenseNum + ",";
                    queue.Enqueue(cmd);
                }
            }
            return temp + "]";
        }
        private string QueueDisplay(Queue<NumberMachineNode> queue)
        {
            string temp = "[";
            if (queue != null && queue.Count != 0)
            {
                NumberMachineNode node = null;
                for (int i = 0; i < queue.Count; i++)
                {
                    node = queue.Dequeue();
                    temp += node.LicenseNum + ",";
                    queue.Enqueue(node);
                }
            }
            return temp + "]";
        }

        /// <summary>
        /// 获取一个停取车指令
        /// </summary>
        public AbstractMessage GetMessage()
        {
            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "号牌队列：" + QueueDisplay(LicenseQueue));
            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "停指队列：" + QueueDisplay(StoreCmdQueue));
            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "取指队列：" + QueueDisplay(FetchCmdQueue));
            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "合法停指队列：" + QueueDisplay(ValidStoreCmdQueue));
            Log.WriteLog(LogType.NOT_DATABASE, LogFile.LOG, "预约队列：" + QueueDisplay(BookParkingQueue));
            lock (ExecutableCmdQueue)
            {
                return (Command)ExecutableCmdQueue.Dequeue();
            }
        }

        /// <summary>
        /// 功能一：由核心线程调用，传入已存车完成需要标记的号牌
        /// 功能二：添加手动停取车指令
        /// </summary>
        public void SetMessage(AbstractMessage message)
        {
            //号牌机重置消息
            if (message.GetType().Equals(typeof(NumberMachineMessage)))
            {
                if (numMachineHandle != null)
                {
                    numMachineHandle.SetMessage(message);
                }
            }
            //手动停取车指令
            else if (message.GetType().Equals(typeof(Command)))
            {
                Command command = (Command)message;
                //command.garageID = 0;
                //command.parkingRecordsID = 0;
                command.TimeRecord = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                //input user command
                if (command != null)
                {
                    if (command.returnedCount > 0)
                    {
                        if (command.commandType.Equals('s'))
                        {
                            lock (ValidStoreCmdQueue)
                            {
                                ValidStoreCmdQueue.Enqueue((Command)command.Clone());
                            }
                            if (command.returnedCount < 10000)
                            {
                                UILogServer.ins.info("收到退回的停车指令：" + command.LicenseNum);
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到退回的停车指令：" + command.LicenseNum);
                            }
                        }
                        else if (command.commandType.Equals('f'))
                        {
                            lock (FetchCmdQueue)
                            {
                                FetchCmdQueue.Enqueue((Command)command.Clone());
                            }
                            if (command.returnedCount < 10000)
                            {
                                UILogServer.ins.info("收到退回的取车指令：" + command.LicenseNum);
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到退回的取车指令：" + command.LicenseNum);
                            }
                        }
                    }
                    else
                    {
                        if (!ConnectionPoolManager.malfunctionLocal)
                        {
                            if (command.commandType.Equals('f'))
                            {
                                lock (FetchCmdQueue)
                                {
                                    FetchCmdQueue.Enqueue((Command)command.Clone());
                                }
                                UILogServer.ins.info("收到手动的取车指令：" + command.LicenseNum);
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到手动的取车指令：" + command.LicenseNum);
                            }
                            else if (command.commandType.Equals('s'))
                            {
                                UserCmdMatch(command);
                                UILogServer.ins.info("收到手动的停车指令：" + command.LicenseNum);
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到手动的停车指令：" + command.LicenseNum);
                            }
                            else
                            {
                                Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "收到无法识别的手动用户指令：" + command.LicenseNum);
                                UILogServer.ins.error("收到无法识别的手动用户指令");
                            }
                        }
                        else
                        {
                            Log.WriteLog(LogType.NOT_DATABASE, LogFile.INFO, "本地数据库异常，暂停接收手动指令" + command.LicenseNum);
                            UILogServer.ins.error("本地数据库异常，暂停接收关于" + command.LicenseNum + "的手动指令");
                        }
                    }
                }
            }
        }

        public void Start()
        {
            license = null;
            webMsg = null;
            Task.Factory.StartNew(() =>
            {
                ReceiveFromWebThread();
            });
            Task.Factory.StartNew(() =>
            {
                Run();
            });
        }

        public void Stop()
        {
            isClosing = true;
        }
    }
}
