using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace StandardScene.MagCarSimulator
{
    public sealed class MagCarSimHost : IDisposable
    {
        private readonly object _syncRoot = new object();
        private readonly List<MagCarSimVehicle> _vehicles = new List<MagCarSimVehicle>();
        private Timer _timer;
        private SimpleLiteMap _map;
        private int _tickBusy;

        public MagCarSimHost(MagCarSimConfig config)
        {
            Config = config ?? throw new ArgumentNullException(nameof(config));
        }

        public MagCarSimConfig Config { get; }
        public SimpleLiteMap Map => _map;
        public bool IsListening { get; private set; }
        public bool IsLooping { get; private set; }

        public IReadOnlyList<MagCarSimVehicle> Vehicles
        {
            get
            {
                lock (_syncRoot)
                {
                    return _vehicles.ToList();
                }
            }
        }

        public SimpleLiteMap LoadMap(string path)
        {
            var map = SimpleLiteMap.Load(path, Config.UseTagValueAsNode);
            var controlPath = MagControlAreaFile.GuessPath(map.FilePath, Config.ControlAreasPath);
            var controlSites = MagControlAreaFile.LoadSiteIds(controlPath);
            map.AddSingleOccupancySites(controlSites);
            lock (_syncRoot)
            {
                _map = map;
                Config.MapPath = map.FilePath;
                if (!string.IsNullOrWhiteSpace(controlPath))
                {
                    Config.ControlAreasPath = controlPath;
                }

                foreach (var vehicle in _vehicles)
                {
                    vehicle.AttachMap(map);
                }
            }

            var exclusive = map.ListSingleOccupancySiteIds();
            MagCarSimLog.WriteLine(
                $"[{DateTime.Now:HH:mm:ss.fff}] 已加载地图 {map.FileName}，站点 {map.Sites.Count}，路径 {map.Tracks.Count}，停止/交管点 {(exclusive.Count == 0 ? "无" : string.Join(",", exclusive))}");
            return map;
        }

        public void RebuildVehicles(IEnumerable<MagCarSimVehicleConfig> configs)
        {
            if (IsListening)
            {
                throw new InvalidOperationException("请先停止模拟再改车辆列表");
            }

            lock (_syncRoot)
            {
                DisposeVehiclesUnlocked();
                foreach (var config in configs ?? Array.Empty<MagCarSimVehicleConfig>())
                {
                    if (config == null)
                    {
                        continue;
                    }

                    var vehicle = new MagCarSimVehicle(Clone(config), Config);
                    if (_map != null)
                    {
                        vehicle.AttachMap(_map);
                    }

                    _vehicles.Add(vehicle);
                }
            }
        }

        public void StartListen()
        {
            List<MagCarSimVehicle> vehicles;
            SimpleLiteMap map;
            lock (_syncRoot)
            {
                if (IsListening)
                {
                    return;
                }

                if (_map == null)
                {
                    throw new InvalidOperationException("请先加载 SimpleLite 地图");
                }

                if (_vehicles.Count == 0)
                {
                    throw new InvalidOperationException("没有可启动的车辆");
                }

                EnsureUniquePortsUnlocked();
                vehicles = _vehicles.ToList();
                map = _map;
                EnsureTimerUnlocked();
                IsListening = true;
            }

            foreach (var vehicle in vehicles)
            {
                vehicle.AttachMap(map);
                vehicle.StartListen();
            }
        }

        public void StartLoops()
        {
            if (!IsListening)
            {
                StartListen();
            }

            List<MagCarSimVehicle> vehicles;
            lock (_syncRoot)
            {
                vehicles = _vehicles.ToList();
                IsLooping = true;
            }

            var now = DateTime.Now;
            foreach (var group in vehicles.GroupBy(RingKey).OrderBy(g => g.Key))
            {
                var members = group.OrderBy(v => v.VehicleCode).ToList();
                var dispatchAt = now;
                foreach (var vehicle in members)
                {
                    vehicle.StartLoop(dispatchAt);
                    dispatchAt = dispatchAt.AddMilliseconds(StaggerMs(vehicle));
                }

                MagCarSimLog.WriteLine(
                    $"[{DateTime.Now:HH:mm:ss.fff}] 同环依次发车 {members[0].Name} 等 {members.Count} 台，间隔 {StaggerMs(members[0])}ms");
            }
        }

        public void StopAll()
        {
            Timer timer;
            List<MagCarSimVehicle> vehicles;
            lock (_syncRoot)
            {
                timer = _timer;
                _timer = null;
                IsListening = false;
                IsLooping = false;
                vehicles = _vehicles.ToList();
            }

            try
            {
                timer?.Dispose();
            }
            catch
            {
            }

            foreach (var vehicle in vehicles)
            {
                vehicle.StopLoop();
                vehicle.StopListen();
            }
        }

        public bool TryRelease(ushort vehicleCode)
        {
            lock (_syncRoot)
            {
                var vehicle = _vehicles.FirstOrDefault(v => v.VehicleCode == vehicleCode);
                if (vehicle == null)
                {
                    return false;
                }

                vehicle.Release();
                return true;
            }
        }

        public List<MagCarSimVehicleSnapshot> Snapshots()
        {
            lock (_syncRoot)
            {
                return _vehicles.Select(v => v.Snapshot()).ToList();
            }
        }

        public void Dispose()
        {
            StopAll();
            lock (_syncRoot)
            {
                DisposeVehiclesUnlocked();
            }
        }

        private void EnsureTimerUnlocked()
        {
            _timer ??= new Timer(_ => OnTick(), null, 50, 50);
        }

        private void OnTick()
        {
            if (Interlocked.Exchange(ref _tickBusy, 1) != 0)
            {
                return;
            }

            try
            {
                List<MagCarSimVehicle> vehicles;
                SimpleLiteMap map;
                lock (_syncRoot)
                {
                    vehicles = _vehicles.ToList();
                    map = _map;
                }

                var occupancy = SnapshotExclusiveOccupancy(map, vehicles);
                var now = DateTime.Now;
                foreach (var vehicle in vehicles)
                {
                    vehicle.Tick(now, occupancy);
                }
            }
            finally
            {
                Interlocked.Exchange(ref _tickBusy, 0);
            }
        }

        private void EnsureUniquePortsUnlocked()
        {
            var seen = new HashSet<int>();
            foreach (var vehicle in _vehicles)
            {
                if (!seen.Add(vehicle.ListenPort))
                {
                    throw new InvalidOperationException($"端口 {vehicle.ListenPort} 被多辆车占用");
                }
            }
        }

        private static Dictionary<int, ushort> SnapshotExclusiveOccupancy(
            SimpleLiteMap map,
            IEnumerable<MagCarSimVehicle> vehicles)
        {
            var occupancy = new Dictionary<int, ushort>();
            if (map == null)
            {
                return occupancy;
            }

            foreach (var vehicle in vehicles)
            {
                if (vehicle.CurrentSiteId <= 0 ||
                    !map.SingleOccupancy(vehicle.CurrentSiteId) ||
                    occupancy.ContainsKey(vehicle.CurrentSiteId))
                {
                    continue;
                }

                occupancy[vehicle.CurrentSiteId] = vehicle.VehicleCode;
            }

            return occupancy;
        }

        private int StaggerMs(MagCarSimVehicle vehicle)
        {
            if (Config.LaunchStaggerMs > 0)
            {
                return Math.Max(200, Config.LaunchStaggerMs);
            }

            var interval = vehicle.Config.IntervalMs > 0 ? vehicle.Config.IntervalMs : Config.DefaultIntervalMs;
            return Math.Max(200, interval);
        }

        private static string RingKey(MagCarSimVehicle vehicle)
        {
            var ids = vehicle.Config.LoopSiteIds;
            if (ids != null && ids.Count > 0)
            {
                return string.Join("-", ids);
            }

            return $"s{vehicle.Config.StartSiteId}-e{vehicle.Config.EndSiteId}";
        }

        private void DisposeVehiclesUnlocked()
        {
            foreach (var vehicle in _vehicles)
            {
                vehicle.Dispose();
            }

            _vehicles.Clear();
        }

        private static MagCarSimVehicleConfig Clone(MagCarSimVehicleConfig source)
        {
            return new MagCarSimVehicleConfig
            {
                Name = source.Name,
                VehicleCode = source.VehicleCode,
                ListenPort = source.ListenPort,
                StartSiteId = source.StartSiteId,
                EndSiteId = source.EndSiteId,
                IntervalMs = source.IntervalMs,
                LoopSiteIds = source.LoopSiteIds == null ? null : new List<int>(source.LoopSiteIds)
            };
        }
    }
}
