using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SimpleLite.RCS;
using SimpleLite.RCS.CarTypes;
using SimpleLite.CADTools;
using SimpleLite.Props;
using SimpleLite.UI;
using SimpleCore;
using SimpleCore.Compiler;
using SimpleCore.Library;
using SimpleCore.PropType;
using SimpleCore.Traffic;
using StandardScene.InterLock;
using StandardScene.Model;
using StandardScene.Utils;
using static StandardScene.Chained.ChainedDeliveryMission;

namespace StandardScene
{
    /// <summary>
    /// 插件本地化设置
    /// </summary>
    public class CustomOperationsBeforeLoading
    {
        public static void Set()
        {
            ///死锁提醒
            TrafficControl.OnDeadLock = (loopingCar) =>
            {
                try
                {
                    var cars = string.Join(",", loopingCar.Select(p => $"{p.id}"));
                    var msg = $"小车:{cars}间发生死锁" +
                              $"请及时人工介入处理!!!!";
                    CycleUiHelper.Alert("死锁提醒", msg);
                }
                catch { }
            };
        }
    }
    public class NoReflectionApi : Attribute
    {

    }

    public class ReflectionApiWithParameter : Attribute
    {
        public string name;
        public string desc;
        public string Hint;
    }

    public static class Commons
    {
        public static VehicleStatus GetVehicleStatus(Car car)
        {
            if(car is DummyCar && car.lstatus.Contains("上线"))
                return VehicleStatus.Normal;
            if (car.lstatus.Contains("正常但未初始化")||car.lstatus.Contains("Normal but not initialized"))
                return VehicleStatus.NeedInit;
            if (car.lstatus.Contains("正常") || car.lstatus.Contains("Normal")||car.lstatus.Contains("上线"))
                return VehicleStatus.Normal;
            if (car.lstatus.Contains("自动驾驶系统失联") || car.lstatus.Contains("Autonomous driving system lost"))
                return VehicleStatus.Offline;
            return VehicleStatus.Unknown;
        }

        /// <summary>
        /// 获取车辆状态
        /// </summary>
        /// <param name="car">车辆</param>
        /// <param name="key">状态-键</param>
        /// <returns>状态-值</returns>
        public static string GetCarStatus(Car car, string key)
        {
            string value = "0";
            if (car == null) return value;
            if (car.status.enums.TryGetValue(key, out var valueStr))
                value = valueStr;
            return value;
        }

        /// <summary>
        /// 添加标签
        /// </summary>
        /// <param name="item"></param>
        /// <param name="tag"></param>
        /// <param name="value"></param>
        public static void AddOrUpdateTag<T>(T item, string tag, string value) where T : TagSet
        {
            if (item.Contains(tag))
                item.Remove(tag);
            item.Add(tag, value);
        }

        /// <summary>
        /// 删除标签
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item"></param>
        /// <param name="tag"></param>
        public static void DeleteTag<T>(T item, string tag) where T : TagSet
        {
            if (item.Contains(tag))
            {
                item.Remove(tag);
            }
        }

        /// <summary>
        /// 清空标签
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item"></param>
        public static void ClearTags<T>(T item) where T : TagSet
        {
            item.Clear();
        }

        /// <summary>
        /// 添加或更新字段
        /// </summary>
        /// <param name="car"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        public static void AddOrUpdateCarField(Car car, string field, string value)
        {
            if (car.fields.ContainsKey(field))
            {
                car.fields.Remove(field);
                car.fields.Add(field, value);
            }
            else
                car.fields.Add(field, value);
        }

        /// <summary>
        /// 删除字段
        /// </summary>
        /// <param name="car"></param>
        /// <param name="field"></param>
        public static void DeleteCarField(Car car, string field)
        {
            if (car.fields.ContainsKey(field))
            {
                car.fields.Remove(field);
            }
        }

        /// <summary>
        /// 清空字段
        /// </summary>
        /// <param name="car"></param>
        public static void ClearCarFields(Car car)
        {
            car.fields.Clear();
        }

        /// <summary>
        /// 添加或更新site的字段
        /// </summary>
        /// <param name="site"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        public static void AddOrUpdateSiteField(Site site, string field, string value)
        {
            if (site.fields.ContainsKey(field))
            {
                site.fields.Remove(field);
                site.fields.Add(field, value);
            }
            else
                site.fields.Add(field, value);
        }

        /// <summary>
        /// 删除site的字段
        /// </summary>
        /// <param name="site"></param>
        /// <param name="field"></param>
        public static void DeleteSiteField(Site site, string field)
        {
            if (site.fields.ContainsKey(field))
            {
                site.fields.Remove(field);
            }
        }

        /// <summary>
        /// 清空site的字段
        /// </summary>
        /// <param name="site"></param>
        public static void ClearSiteFields(Site site)
        {
            site.fields.Clear();
        }

        /// <summary>
        /// 检查给定字符串是否为合法的 HTTP/HTTPS URL（至少包含协议与主机）。
        /// </summary>
        /// <param name="url">待校验的 URL 字符串</param>
        /// <returns>true 表示 URL 合法</returns>
        public static bool IsValidHttpUrl(string url)
        {
            if (string.IsNullOrWhiteSpace(url)) return false;
            if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false;
            if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) return false;
            if (string.IsNullOrEmpty(uri.Host)) return false;
            return true;
        }

        public static double CarValue(Car car, string key)
        {
            if (car.fields.TryGetValue("electricCurrent", out var strelectricCurrent))
                return double.Parse(strelectricCurrent);
            if (key == "Soc" && (car.name.Contains("模拟") || car.name.ToLower().Contains("sim")))
            {

                if (car.fields.TryGetValue("Soc", out var strSoc))
                    return double.Parse(strSoc);

                return 100;
            }

            var value = "0";
            if (car.status.enums.TryGetValue(key, out var valueStr))
                value = valueStr;
            return double.Parse(value);
        }

        /// <summary>
        /// 添加或更新mission的字段
        /// </summary>
        /// <param name="mission"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        public static void AddOrUpdateMissionField(Mission mission, string field, string value)
        {
            if (mission.fields.ContainsKey(field))
            {
                mission.fields.Remove(field);
                mission.fields.Add(field, value);
            }
            else
                mission.fields.Add(field, value);
        }

        /// <summary>
        /// 删除mission的字段
        /// </summary>
        /// <param name="mission"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public static void DeleteMissionField(Mission mission, string field)
        {
            if (mission.fields.ContainsKey(field))
                mission.fields.Remove(field);
        }
        public static List<AbstractCar> GetOnlineCars()
        {
            return SimpleLib.GetAllCars().Where(c => c.GetLastSite() != -1 && Commons.GetVehicleStatus((Car)c)== VehicleStatus.Normal).ToList();
        }
        public static bool IsSceneSiteByCode(int[] siteCodes)
        {
            try
            {
                foreach (var siteCode in siteCodes)
                {
                    var site = SimpleLib.GetAllSites().FirstOrDefault(s => s.id == siteCode);
                    if (site == null) return false;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($@"IsSceneSite 校验报错=>{ex.Message}");
                return false;
            }
            return true;
        }

        public static readonly object PlanSession = new object();

        public static int SelectCar(AbstractCar car, bool enableGoingStandbyCar = false, bool needCharge = false, bool checkHoldCar = false)
        {
            var carHaveCoordination = (Car)car;
            var conditions = car.GetLastSite() != -1 && GetVehicleStatus((Car)car)== VehicleStatus.Normal&&
                             !car.tags.Contains("changePriority") &&
                             !car.tags.Contains("priority") && !car.tags.Contains("occupied")
                             && !car.tags.Contains("agvOffline")
                             && car.status.pendingLocks.Length == 0 && !car.tags.Contains("deliver") &&//pendingLocks临时增加解决车接多任务问题
                             (!car.tags.Contains("shouldCharge") || needCharge)
                             && !car.tags.Contains("currentWorkStep") && !car.fields.ContainsKey("StopAccept") && car.tags.Contains("Online"); //巡航任务未完成的车
            // Mag2Car 联调：UDP Online 即可调度，不强制 lstatus=上线（模拟器常报 state=0 未准备）
            var isMag2Car = string.Equals(car.GetType().Name, "Mag2Car", StringComparison.Ordinal);
            if (car.name.Contains("模拟") || isMag2Car)
            {
                conditions = car.GetLastSite() != -1 &&
                             (!car.tags.Contains("holdCar") || !checkHoldCar) && !car.tags.Contains("occupied")
                             && (!isMag2Car || car.tags.Contains("Online"))
                             && car.status.pendingLocks.Length == 0 && !car.tags.Contains("deliver") &&//pendingLocks临时增加解决车接多任务问题
                             (!car.tags.Contains("shouldCharge") || needCharge)
                             && !car.tags.Contains("currentWorkStep") && !car.fields.ContainsKey("StopAccept"); //巡航任务未完成的车
            }
            //((carHaveCoordination.haveCoordination || car.name.Contains("模拟")) && (!car.tags.Contains("shouldCharge") || needCharge));
            if (conditions) return 1;

            if (enableGoingStandbyCar && car.tags.TryGetValue("dest", out var dst) &&
                int.TryParse(dst, out var dstId) &&
                SimpleLib.GetSite(dstId).fields.ContainsKey(GetGiveWayType((Car)car))) return 1;
            return -1;
        }

        private static string GetGiveWayType(Car car)
        {
            if (car.fields.ContainsKey("mover")) return "standbyMover";
            if (car.fields.ContainsKey("loader")) return "standbyLoader";
            return "giveWay";
        }
        public static bool SiteHaveTask(Site site)
        {
            var siteHaveTask = SimpleLib.GetAllCars().ToList().FindAll(o =>
            {
                if (o.status.pendingLocks.Length > 0)
                {
                    if (o.status.pendingLocks.Contains(site.id))
                    {
                        return true;
                    }
                }
                if (o.status.holdingLocks.Length > 0)
                {
                    if (o.status.holdingLocks.Contains(site.id))
                    {
                        return true;
                    }
                }


                return false;
            }).Count != 0;
            return siteHaveTask;
        }
        public static SegmentPlan GetNearestPlan(Car car, Func<Site, bool> test, int srcId = -1, bool disableConflict = false)
        {
            var curRouteLength = float.MaxValue;
            SegmentPlan targetPlan = null;
            foreach (var site in SimpleLib.GetAllSites())
            {
                if (!test(site)) continue;
                bool occupied = false;
                foreach (var cc in SimpleLib.GetAllCars())
                {
                    if (cc != car)
                    {
                        if (cc.tags.Contains("dest") && cc.tags.IsEqual("dest", site.id.ToString()) ||
                            cc.status.holdingLocks.Contains(site.id))
                            occupied = true;
                    }
                }

                if (occupied) continue;

                var mPlan = new SegmentPlan { usingCar = car };
                if (disableConflict)
                    mPlan.fields["forbid_cross"] = "false";
                try
                {
                    var newRouteLength = mPlan.FindRoute(
                        SimpleLib.GetSite(srcId == -1 ? car.GetLastSite() : srcId),
                        SimpleLib.GetSite(site.id));

                    if (newRouteLength < curRouteLength)
                    {
                        curRouteLength = newRouteLength;
                        targetPlan = mPlan;
                    }
                }
                catch
                {
                    // ignored
                }
            }

            return targetPlan;
        }

        public static void NearestTask(List<Delivery> deliveries, List<Delivery> runningDeliveries = null)
        {
            try
            {
                var curW = float.MaxValue;
                var priority = int.MinValue;
                Delivery priorityNextDelivery = null;
                Delivery deliveryTask = null;

                //车不是空闲车，但这个任务结束的时候离另一个将要执行的任务很近。
                //（通常来说是同一个工序的，除非单纯地返空托（或路线规划失败，跳别的任务了未按优先级执行）在仓库）
                // 车空闲
                foreach (var car in SimpleLib.GetAllCars().OfType<Car>()) //找到那个车 距离待执行任务最近
                {
                    DeleteTag(car.tags, "changePriority");
                    //车的状态是OK的
                    if (car.GetLastSite() == -1 || car.tags.Contains("agvOffline") || car.tags.Contains("occupied")) continue;
                    //车已经被分配过任务了
                    if (deliveries.FirstOrDefault(t => t.UsingCar == car) != null) continue;

                    int dst = 0;
                    #region ObsoleteCode
                    //if (car.tags.ContainsKey("occupied")) //说明是正在执行任务的车
                    //{

                    //    if (car.tags.ContainsKey("dest")
                    //        && deliverys.Where(d => d.usingCar != null && d.usingCar == car) == null  //并且这个车没有需要等待的任务执行的任务
                    //                                         //还存在一种情况车被选了但中途有高优先级的任务进来
                    //        )
                    //    {
                    //        //这个车正在执行的任务
                    //        dst = int.Parse(car.tags["dest"]);
                    //    }
                    //    else
                    //    {
                    //        //这个车已经有需要等待的任务了。
                    //        continue;
                    //    }


                    //}
                    //else //车空闲
                    //{
                    //  dst = car.GetLastSite();
                    //}
                    //无效
                    #endregion
                    dst = car.GetLastSite();
                    if (dst == -1)
                    {
                        //输出日志 变更dst;
                        dst = car.status.holdingLocks.First();
                        Diagnosis.Post($"调度小车{car.name}:{car.id}的GetLastSite 为 -1 。变更 holdingLocks.First() 为小车初始点{dst}");
                    }
                    //空闲的车 //如果有多个那个离的最近

                    foreach (var delivery in deliveries.Where(d => d.UsingCar == null))
                    {
                        //车是否能接到该任务。
                        if (car.fields.ContainsKey("group") && !string.IsNullOrEmpty(car.fields["group"]) && !car.fields["group"].Contains(delivery.Group))
                            continue;

                        var mPlan = new SegmentPlan { usingCar = car };

                        try
                        {
                            var myW = mPlan.FindRoute(
                                SimpleLib.GetSite(dst),
                                SimpleLib.GetSite(delivery.Src));

                            //找到最近的，
                            if (myW < curW)
                            {
                                curW = myW;
                                //找到优先级最高的。且距离最近
                                if (delivery.Priority >= priority)
                                {
                                    priorityNextDelivery = delivery;
                                    priority = delivery.Priority;
                                    Diagnosis.Post($"{priorityNextDelivery.Id}/{priorityNextDelivery.Group}/{priority}", " delivery.priority");
                                }

                                //找到距离最近的。
                                Delivery nextDelivery = delivery;

                                if (nextDelivery != priorityNextDelivery) //并且这两个任务不同  优先级高的比较远
                                {
                                    if (runningDeliveries != null)
                                    {
                                        //优先级高的先执行
                                        Delivery deliveryRunning = null; // runningDeliveries.Where(d => d.group == priorityNextDelivery.group).FirstOrDefault();

                                        //有车正在执行优先级高的任务。有的话就不让车过去
                                        if (deliveryRunning != null) //true
                                        {
                                            Diagnosis.Post($"{deliveryRunning.Id}/{deliveryRunning.UsingCar?.id}/" +
                                                $"{deliveryRunning.Group}/{deliveryRunning.Priority}", "deliveryRunning");
                                            deliveryTask = nextDelivery;
                                        }
                                        else
                                        {
                                            deliveryTask = priorityNextDelivery;
                                            Diagnosis.Post($"{priorityNextDelivery.Id}/{priorityNextDelivery.UsingCar?.id}/" +
                                                $"{priorityNextDelivery.Group}/{priorityNextDelivery.Priority}", "priorityNextDelivery1");

                                        }
                                    }
                                    else
                                    {
                                        deliveryTask = priorityNextDelivery;
                                        Diagnosis.Post($"{priorityNextDelivery.Id}/{priorityNextDelivery.UsingCar?.id}/" +
                                            $"{priorityNextDelivery.Group}/{priorityNextDelivery.Priority}", "priorityNextDelivery2");

                                    }
                                }
                                else
                                {
                                    deliveryTask = nextDelivery;
                                    Diagnosis.Post($"{nextDelivery.Id}/{nextDelivery.UsingCar?.id}/" +
                                           $"{nextDelivery.Group}/{nextDelivery.Priority}", "nextDelivery");
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message + ex.ToString());
                        }
                    }
                    if (deliveryTask != null)
                    {
                        deliveryTask.UsingCar = car;
                        deliveryTask.Priority = 50;
                    }
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.Message + ex.ToString());
            }
        }

        public static async Task GoSite(AbstractCar car, Site targetSite, int step, string action = "/", bool reverse = false)
        {

            try
            {
                var plan = new SegmentPlan
                {
                    usingCar = car,
                    fields =
                    {
                        ["reverse"] = reverse.ToString(),
                        ["action"] = action,
                        ["allow_destination_on_route"] = "true"
                    }
                };
                /*     if (stations!=null && stations.Count()>=1)
                     {
                         var finishLeftPtlSites = SimpleLib.GetAllSites().Where(site => site.name.Contains("TaskStation")).ToList();
                         foreach (var site in finishLeftPtlSites)
                         {
                             var stationCode = site.name.Split('-')[1];
                             if (stations.Contains(stationCode))
                             {
                                 site.fields.Add("askInfons1", "1");
                             }
                         }
                     }*/
                plan.FindRoute(SimpleLib.GetSite(car.GetLastSite()), targetSite);
                //plan.HintNotEnding();
                var program = plan.Compile("move");
                car.tags.Add("occupied", $"go{targetSite.id}");
                Console.WriteLine($">>Script:{program.script}");
                var tsk = program.Queue();
                /*  if (stations != null && stations.Count() >= 1)
                        {
                               var finishLeftPtlSites = SimpleLib.GetAllSites().Where(site => site.name.Contains("TaskStation")).ToList();
                               foreach (var site in finishLeftPtlSites)
                               {
                                   var stationCode = site.name.Split('-')[1];
                                   if (stations.Contains(stationCode))
                                   {
                                       site.fields.Remove("askInfons1");
                                   }
                               }
                         }*/
                await tsk;
                car.tags.Remove("occupied");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "重新执行");
                Thread.Sleep(3000);
            }
        }

        public static SegmentPlan GenerateEscapePlan(SegmentPlan dstPlan, Func<Site, bool> escapeSiteCondition = null)
        {
            var curW = float.MaxValue;
            var car = dstPlan.usingCar;

            var escapedSites = SimpleLib.GetAllCars().Where(cc => cc.status.escape.Length > 0)
                .Select(cc => cc.status.escape.Last()).ToHashSet();

            SegmentPlan planEsc = null;
            foreach (var site in SimpleLib.GetAllSites())
            {
                if (site.id == dstPlan.Destination.id || site.id == dstPlan.Source.id) continue;
                if (escapedSites.Contains(site.id)) continue;
                if (escapeSiteCondition != null && !escapeSiteCondition(site)) continue;

                bool occupied = false;
                foreach (var cc in SimpleLib.GetAllCars())
                {
                    if (cc != car)
                    {
                        if (cc.status.holdingLocks.Contains(site.id) ||
                            (cc.tags.Contains("dest") && cc.tags["dest"] == site.id.ToString())) occupied = true;
                    }
                }

                if (occupied) continue;

                TryGeneratePlan(dstPlan.usingCar, dstPlan.Destination, site, ref curW, ref planEsc);
            }

            return planEsc;
        }
        private static void TryGeneratePlan(AbstractCar car, Site srcSite, Site dstSite, ref float curW, ref SegmentPlan generatedPlan)
        {
            var mPlan = new SegmentPlan { usingCar = car, findLoop = false, fields = new Dictionary<string, string>() };
            mPlan.fields["forbid_cross"] = "false";
            try
            {
                var actuallyFindRoute = false;

                var myw = TryGetWeight(srcSite, dstSite);
                if (myw < 0)
                {
                    myw = mPlan.FindRoute(srcSite, dstSite);
                    AddWeight(srcSite, dstSite, myw);
                    actuallyFindRoute = true;
                }

                if (myw < curW)
                {
                    if (!actuallyFindRoute) mPlan.FindRoute(srcSite, dstSite);
                    curW = myw;
                    generatedPlan = mPlan;
                }
            }
            catch
            {
                // ignored
            }
        }
        public static Dictionary<(Site, Site), float> weightDictionary = new();
        public static float TryGetWeight(Site s1, Site s2)
        {
            lock (weightDictionary)
            {
                if (weightDictionary.TryGetValue((s1, s2), out var w)) return w;
                if (weightDictionary.TryGetValue((s2, s1), out var ww)) return ww;
            }
            return -1;
        }

        public static void AddWeight(Site s1, Site s2, float w)
        {
            lock (weightDictionary) weightDictionary[(s1, s2)] = w;
        }

      

        public static SegmentPlan SimpleToNearestPlan(AbstractCar car, Func<Site, bool> test, int srcID = -1)
        {
            var curw = float.MaxValue;
            SegmentPlan targetPlan = null;
            var srcSite = SimpleLib.GetSite(srcID == -1 ? car.GetLastSite() : srcID);

            foreach (var site in SimpleLib.GetAllSites())
            {
                if (!test(site)) continue;
                if (site.id == srcSite.id) continue;
                bool occupied = false;
                foreach (var cc in SimpleLib.GetAllCars())
                {
                    // if (cc != car)//当前
                    {
                        if (cc.status.holdingLocks.Contains(site.id) ||
                            (cc.tags.Contains("dest") && cc.tags["dest"] == site.id.ToString())) occupied = true;
                    }
                }

                if (occupied) continue;
                TryGeneratePlan(car, srcSite, site, ref curw, ref targetPlan);
            }

            return targetPlan;
        }
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);


        private static bool ShowConsole = false;

        public static void ShowConsoleFun()
        {
            ShowConsole = !ShowConsole;
            var handle = GetConsoleWindow();
            int n = ShowConsole ? 0 : 5;
            Console.WriteLine(n);
            ShowWindow(handle, n);

        }
        public static void HideConsoleFun()
        {
            var handle = GetConsoleWindow();
            ShowWindow(handle, 0);
        }
    }
}
