using Plane.Communication;
using Plane.Geography;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using static Plane.Copters.Constants;

namespace Plane.Copters
{
    /// <summary>
    /// 虚拟的 <see cref="ICopter"/> 实现。
    /// </summary>
    [DebuggerDisplay("Name={Name}")]
    public partial class FakeCopter : CopterImplSharedPart, IFakeCopter
    {
        /// <summary>
        /// 心跳间隔，单位为毫秒。
        /// </summary>
        private const int HEARTBEAT_INTERVAL = 200;

        /// <summary>
        /// 在一个更新间隔中的最大移动距离。
        /// </summary>
        private const float MAX_MOVE_IN_INTERVAL = MAX_VEL * UPDATE_INTERVAL / 1000;

        /// <summary>
        /// 高速模式下，在一个更新间隔中的最大移动距离。
        /// </summary>
        private const float MAX_MOVE_IN_INTERVAL_FAST = MAX_MOVE_IN_INTERVAL * 4;

        /// <summary>
        /// 更新虚拟飞行器状态的时间间隔，单位为毫秒。
        /// </summary>
        private const int UPDATE_INTERVAL = 100;

        /// <summary>
        /// 对飞行器的模拟是否正在运行。
        /// </summary>
        private bool _isRunning = false;

        /// <summary>
        /// 上次计算速度时的位置。
        /// </summary>
        private ILocation _lastCalcSpeedPoint;

        /// <summary>
        /// 上次计算速度的时间。
        /// </summary>
        private DateTime _lastCalcSpeedTime;

        /// <summary>
        /// 高速模式下，按比例缩放过的在一个更新间隔中的最大移动距离。
        /// </summary>
        private float _scaledFastMaxMoveInInterval = MAX_MOVE_IN_INTERVAL_FAST;

        /// <summary>
        /// 按比例缩放过的在一个更新间隔中的最大移动距离。
        /// </summary>
        private float _scaledMaxMoveInInterval = MAX_MOVE_IN_INTERVAL;

        /// <summary>
        /// 速度缩放比例。
        /// </summary>
        private float _speedScale = 1;

        /// <summary>
        /// 自动起飞的目标高度。
        /// </summary>
        private int _takeOffTargetAltitude;

        /// <summary>
        /// FlyTo 的目标高度。
        /// </summary>
        private float _targetAlt;

        /// <summary>
        /// FlyTo 的目标纬度。
        /// </summary>
        private double _targetLat;

        /// <summary>
        /// FlyTo 的目标经度。
        /// </summary>
        private double _targetLng;


        /// <summary>
        /// FlyTo 的目标经度。
        /// </summary>
        private bool _ShowLED;


        /// <summary>
        /// 使用 <see cref="SynchronizationContext.Current"/> 创建 <see cref="FakeCopter"/> 实例。
        /// </summary>
        public FakeCopter() : this(SynchronizationContext.Current)
        {
        }

        /// <summary>
        /// 创建 <see cref="FakeCopter"/> 实例。
        /// </summary>
        /// <param name="uiSyncContext">UI 线程的同步上下文。</param>
        /// <param name="takeOffTargetAltitude">自动起飞时的目标高度。</param>
        /// <param name="speedScale">速度缩放比例。</param>
        public FakeCopter(SynchronizationContext uiSyncContext, int takeOffTargetAltitude = 10, float speedScale = 1, IConnection connection = null)
            : base(uiSyncContext)
        {
            Latitude = 23.155382266268134;
            Longitude = 113.45038586296141;

            Elevation = 20;
            this.PropertyChanged += (sender, e) =>
            {
                switch (e.PropertyName)
                {
                    case nameof(Altitude):
                        Elevation = Altitude + 20;
                        break;

                    case nameof(IsUnlocked):
                        if (IsUnlocked)
                        {
                            // 林俊清, 20151029, 在解锁时上次计算速度点、上次计算速度时刻。
                            _lastCalcSpeedPoint = new PLLocation { Latitude = Latitude, Longitude = Longitude, Altitude = Altitude };
                            _lastCalcSpeedTime = DateTime.Now;
                        }
                        break;
                }
            };

            _takeOffTargetAltitude = takeOffTargetAltitude;

            _speedScale = speedScale;
            _scaledMaxMoveInInterval = MAX_MOVE_IN_INTERVAL * _speedScale;
            _scaledFastMaxMoveInInterval = MAX_MOVE_IN_INTERVAL_FAST * _speedScale;

            this.Connection = connection ?? EmptyConnection.Instance;

            // 持续计算并更新虚拟飞行器的状态。
            Task.Run(async () =>
            {
                while (true)
                {
                    if (_isRunning)
                    {
                        Update();
                    }
                    await TaskUtils.Delay(UPDATE_INTERVAL).ConfigureAwait(false);
                }
            });

            // 持续假装收到飞行器发来的心跳。
            Task.Run(async () =>
            {
                while (true)
                {
                    if (_isRunning)
                    {
                        ++HeartbeatCount;

                        IsCheckingConnection = false;

                        if (HeartbeatCount >= 10)
                        {
                            // 收到若干个心跳之后，设置一下 GPS 相关属性。
                            GpsFixType = GpsFixType.Fix3D;
                            GpsHdop = 1;
                            IsGpsAccurate = true;
                            HasSwitchedToGpsMode = true;
                        }

                        _uiSyncContext.Post(() => RaiseHeartbeatReceived(new HeartbeatReceivedEventArgs(HeartbeatCount)));
                    }
                    await TaskUtils.Delay(HEARTBEAT_INTERVAL).ConfigureAwait(false);
                }
            });
        }

        public IConnection Connection { get; set; }

        public string Id { get; set; }

        private int ReturnToLaunchAltitude { get { return _takeOffTargetAltitude; } }

        public Task ConnectAsync()
        {
            IsConnected = true;
            _isRunning = true;
            IsCheckingConnection = true;
            return TaskUtils.CompletedTask;
        }

        public Task DisconnectAsync()
        {
            IsConnected = false;
            _isRunning = false;
            IsCheckingConnection = false;
            return TaskUtils.CompletedTask;
        }

        protected override Task FlyToCoreAsync(double lat, double lng, float alt)
        {
            if (!IsEmergencyHoverActive)
            {
                _targetLat = lat;
                _targetLng = lng;
                _targetAlt = alt;
                Mode = FlightMode.GUIDED;
            }
            return TaskUtils.CompletedTask;
        }

        public Task GetCopterDataAsync()
        {
            return TaskUtils.CompletedTask;
        }

        public Task<float> GetParamAsync(string paramName, int millisecondsTimeout = Timeout.Infinite)
        {
            // TODO: 林俊清, 20150806, 实现仿真的 GetParamAsync。
            return Task.FromResult(0f);
        }

        public Task LockAsync()
        {
            if (IsUnlocked)
            {
                IsUnlocked = false;
            }
            return TaskUtils.CompletedTask;
        }

        public override Task SetChannelsAsync()
        {
            Channel1 = DesiredChannel1 ?? Channel1;
            Channel2 = DesiredChannel2 ?? Channel2;
            Channel3 = DesiredChannel3 ?? Channel3;
            Channel4 = DesiredChannel4 ?? Channel4;
            Channel5 = DesiredChannel5 ?? Channel5;
            Channel6 = DesiredChannel6 ?? Channel6;
            Channel7 = DesiredChannel7 ?? Channel7;
            Channel8 = DesiredChannel8 ?? Channel8;
            return TaskUtils.CompletedTask;
        }

        public override Task SetMobileControlAsync()
        {
            Channel1 = DesiredChannel1 ?? Channel1;
            Channel2 = DesiredChannel2 ?? Channel2;
            Channel3 = DesiredChannel3 ?? Channel3;
            Channel4 = DesiredChannel4 ?? Channel4;
            Channel5 = DesiredChannel5 ?? Channel5;
            Channel6 = DesiredChannel6 ?? Channel6;
            Channel7 = DesiredChannel7 ?? Channel7;
            Channel8 = DesiredChannel8 ?? Channel8;
            Yaw = DesiredYaw ?? Yaw;
            Heading = (short)Yaw;
            return TaskUtils.CompletedTask;
        }

        public Task SetParamAsync(string paramName, float value, int millisecondsTimeout = Timeout.Infinite)
        {
            // TODO: 林俊清, 20150807, 实现仿真的 SetParamAsync。
            return TaskUtils.CompletedTask;
        }
        public bool GetShowLEDAsync()
        {
            return _ShowLED;
        }             
        public Task SetShowLEDAsync(bool Ledon)
        {
            _ShowLED = Ledon;
            RaiseLocationChanged();
            return TaskUtils.CompletedTask;
        }
        public Task SetShowLEDFlashAsync(int Flashtype, int millisecondsTime)
        {
            bool Ledison;
            Ledison = GetShowLEDAsync();
            if (!Ledison)
            {
                 SetShowLEDAsync(true);
                 Task.Delay(millisecondsTime).ConfigureAwait(false);
                 SetShowLEDAsync(false);
            }
            else
            {
                 SetShowLEDAsync(false);
                 Task.Delay(millisecondsTime).ConfigureAwait(false);
                 SetShowLEDAsync(true);
            }
           
            return TaskUtils.CompletedTask;
        }

        public void SetProperties(
            string id = null,   //"Junqing's Drone",
            double? latitude = null,   //23.14973333,
            double? longitude = null,   //113.40974166,
            float? altitude = null,   //0,
            string name = null,   //"林俊清的飞行器",
            byte? batteryPer = null,   //10,
            short? heading = null,   //33,
            bool? isConnected = null,   //true,
            float? pitch = null,   //-70,
            float? roll = null,   //28,
            byte? satCount = null,   //6,
            float? airSpeed = null,   //3.333,
            double? flightDistance = null,   //100.388,
            double? flightDistance2D = null, // 100.88,
            TimeSpan? flightTimeSpan = null)
        {
            if (id != null) Id = id;
            if (name != null) Name = name;
            if (latitude != null) Latitude = latitude.Value;
            if (longitude != null) Longitude = longitude.Value;
            if (altitude != null) Altitude = altitude.Value;
            if (batteryPer != null) BatteryPer = batteryPer.Value;
            if (heading != null) Heading = heading.Value;
            if (isConnected != null) IsConnected = isConnected.Value;
            if (pitch != null) Pitch = pitch.Value;
            if (roll != null) Roll = roll.Value;
            if (satCount != null) SatCount = satCount.Value;
            if (airSpeed != null) AirSpeed = airSpeed.Value;
            if (flightDistance != null) FlightDistance = flightDistance.Value;
            if (flightDistance2D != null) FlightDistance2D = flightDistance2D.Value;
            if (flightTimeSpan != null) FlightTimeSpan = flightTimeSpan.Value;
        }

        public Task StartPairingAsync()
        {
            RaisePairingCompleted(new PairingCompletedEventArgs(true, 0, 0));
            return TaskUtils.CompletedTask;
        }

        public Task StopPairingAsync()
        {
            return TaskUtils.CompletedTask;
        }

        public async Task TakeOffAsync(float alt)
        {
            await UnlockAsync().ConfigureAwait(false);
            _takeOffTargetAltitude = (int)alt;
            await TakeOffAsync().ConfigureAwait(false);
        }

        public async Task UnlockAsync()
        {
            if (!IsUnlocked)
            {
                await SetChannelsAsync(
                    ch1: 1500,
                    ch2: 1500,
                    ch3: 1100,
                    ch4: 1500
                ).ConfigureAwait(false);
                if (await SetModeAsync(FlightMode.ALT_HOLD).ConfigureAwait(false))
                {
                    IsUnlocked = true;
                }
            }
        }

        /// <summary>
        /// 无论在不在紧急悬停状态下都可用的切换模式方法。
        /// </summary>
        /// <param name="mode">模式。</param>
        /// <param name="millisecondsTimeout">超时。</param>
        /// <returns>成功与否。</returns>
        internal override Task<bool> SetModeCoreAsync(FlightMode mode, int millisecondsTimeout = 5000)
        {
            Mode = mode;
            return Task.FromResult(true);
        }

        protected override void UpdateFlightDataIfNeeded()
        {
            if (!TakeOffPoint.IsNullOrEmpty())
            {
                FlightDistance = TakeOffPoint.CalcDistance(this);
                FlightDistance2D = TakeOffPoint.CalcDistance2D(this);
            }
            if (FlightTimeSpan.TotalSeconds > 0)
            {
                if (_lastCalcSpeedPoint.IsNullOrEmpty())
                {
                    if (!this.IsNullOrEmpty())
                    {
                        _lastCalcSpeedPoint = new PLLocation(this);
                        _lastCalcSpeedTime = DateTime.Now;
                    }
                }
                else
                {
                    var movedDistance = _lastCalcSpeedPoint.CalcDistance(this);
                    var movedTime = DateTime.Now - _lastCalcSpeedTime;
                    if (movedDistance >= 3 || movedTime.TotalSeconds >= 3)
                    {
                        var airSpeed = movedDistance / movedTime.TotalSeconds;
                        if (airSpeed < 100)  // 林俊清, 20151023, 速度过大时不正常，经纬度可能有错误。
                        {
                            AirSpeed = (float)airSpeed;
                        }
                        GroundSpeed = AirSpeed;
                        _lastCalcSpeedPoint = new PLLocation(this);
                        _lastCalcSpeedTime = DateTime.Now;
                    }
                }
            }
        }

        private void MoveToPointImmediately(double lat, double lng, float alt)
        {
            Latitude = lat;
            Longitude = lng;
            Altitude = alt;
            Roll = 0;
            Pitch = 0;
        }

        private bool ReachedDestination(double destLat, double destLng, float destAlt) => this.CalcDistance(destLat, destLng, destAlt) < 0.1;

        /// <summary>
        /// 计算并更新虚拟飞行器状态。
        /// </summary>
        private void Update()
        {
            if (IsUnlocked)
            {
                // 飞行器接触地面（只判断 Altitude <= 0）后，如果处于 LAND 模式或者保持 Channel3 < MIN_HOVER_CHANNEL，在一段时间后自动锁定。
                if ((Mode == FlightMode.LAND || Channel3 < MIN_HOVER_CHANNEL) && Altitude <= 0)
                {
                    Task.Run(async () =>
                    {
                        for (int i = 0; i < 4000 / 20; i++)
                        {
                            await TaskUtils.Delay(20).ConfigureAwait(false);
                            if ((Mode != FlightMode.LAND && Channel3 >= MIN_HOVER_CHANNEL) || !IsUnlocked)
                            {
                                return;
                            }
                        }
                        IsUnlocked = false;
                    });
                }
                else
                {
                    switch (Mode)
                    {
                        case FlightMode.ACRO:
                            break;

                        case FlightMode.AUTO:
                            // 暂时只管起飞。以最大速度上升，直到达到目标高度。
                            if (Altitude < _takeOffTargetAltitude)
                            {
                                Altitude += _scaledMaxMoveInInterval;
                            }
                            else
                            {
                                Mode = FlightMode.LOITER;
                            }
                            break;

                        case FlightMode.GUIDED:
                            // 林俊清, 20160317, 指点时也能体感控制若干通道。
                            UpdateWithChannels();
                            UpdateWithDestination(_targetLat, _targetLng, _targetAlt);
                            break;

                        case FlightMode.STABILIZE:
                        case FlightMode.ALT_HOLD:
                        case FlightMode.LOITER:
                            UpdateWithChannels();
                            break;

                        case FlightMode.RTL:
                            if (Altitude < ReturnToLaunchAltitude && !ReachedDestination(Latitude, Longitude, ReturnToLaunchAltitude))
                            {
                                UpdateWithDestination(Latitude, Longitude, ReturnToLaunchAltitude);
                            }
                            else if (Altitude > ReturnToLaunchAltitude && !ReachedDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, Altitude))
                            {
                                UpdateWithDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, Altitude);
                            }
                            else
                            {
                                UpdateWithDestination(TakeOffPoint.Latitude, TakeOffPoint.Longitude, ReturnToLaunchAltitude);
                            }
                            break;

                        case FlightMode.CIRCLE:
                            break;

                        case FlightMode.POSITION:
                            break;

                        case FlightMode.LAND:
                            // 林俊清, 20160317, 降落时也能体感控制若干通道。
                            UpdateWithChannels();
                            // 以最大速度降落，直到高度为 0。
                            if (Altitude > 0)
                            {
                                if (Altitude > _scaledMaxMoveInInterval)
                                {
                                    Altitude -= _scaledMaxMoveInInterval;
                                }
                                else
                                {
                                    Altitude = 0;
                                }
                                Roll = 0;
                                Pitch = 0;
                            }
                            break;

                        case FlightMode.OF_LOITER:
                            break;

                        case FlightMode.TOY:
                            break;

                        default:
                            break;
                    }
                    UpdateFlightDataIfNeeded();
                }
            }
            else
            {
                // TODO: 林俊清, 20151228, 模拟空中锁定。
                // 锁定时直接把速度设为 0。
                AirSpeed = 0;
            }
            _uiSyncContext.Post(() =>
            {
                RaiseLocationChangedIfNeeded();
                RaiseAltitudeChangedIfNeeded();

                RaiseDataStreamReceived(PDataStreamType.GPS_RAW_INT);
                RaiseAttitudeChanged();
            });
        }

        /// <summary>
        /// 根据各个通道值来更新位置、姿态等状态。
        /// </summary>
        private void UpdateWithChannels()
        {
            var maxMove = Mode == FlightMode.LOITER ? _scaledMaxMoveInInterval : _scaledFastMaxMoveInInterval;
            var ch3Delta = Channel3 > MAX_HOVER_CHANNEL ? Channel3 - MAX_HOVER_CHANNEL : Channel3 < MIN_HOVER_CHANNEL ? Channel3 - MIN_HOVER_CHANNEL : 0;
            if (ch3Delta != 0)
            {
                // 更新相对高度。
                Altitude = Math.Max(0, Altitude + maxMove * ch3Delta / MAX_CHANNEL_DELTA);
            }

            var yawRad = Yaw.DegToRad();
            var metersToLocalLng = GeographyUtils.CalcMetersToLngSpan(Latitude);

            var ch1Delta = Channel1 > MAX_HOVER_CHANNEL ? Channel1 - MAX_HOVER_CHANNEL : Channel1 < MIN_HOVER_CHANNEL ? Channel1 - MIN_HOVER_CHANNEL : 0;
            var xDistance = maxMove * ch1Delta / MAX_CHANNEL_DELTA;
            if (ch1Delta != 0)
            {
                // 更新横滚方向的纬度和经度。
                Latitude -= xDistance * Math.Sin(yawRad) * GeographyUtils.METERS_TO_LAT_SPAN;
                Longitude += xDistance * Math.Cos(yawRad) * metersToLocalLng;
                // 更新横滚姿态。
                Roll = MAX_TILT_IN_FLIGHT * ch1Delta / MAX_CHANNEL_DELTA;
            }
            else
            {
                Roll = 0;
            }

            var ch2Delta = Channel2 > MAX_HOVER_CHANNEL ? Channel2 - MAX_HOVER_CHANNEL : Channel2 < MIN_HOVER_CHANNEL ? Channel2 - MIN_HOVER_CHANNEL : 0;
            var yDistance = maxMove * ch2Delta / MAX_CHANNEL_DELTA;
            if (ch2Delta != 0)
            {
                // 更新俯仰方向的纬度和经度。
                Latitude -= yDistance * Math.Cos(yawRad) * GeographyUtils.METERS_TO_LAT_SPAN;
                Longitude -= yDistance * Math.Sin(yawRad) * metersToLocalLng;
                // 更新俯仰姿态。
                Pitch = MAX_TILT_IN_FLIGHT * ch2Delta / MAX_CHANNEL_DELTA;
            }
            else
            {
                Pitch = 0;
            }
        }

        /// <summary>
        /// 根据目的地来更新位置、姿态等状态。
        /// </summary>
        private void UpdateWithDestination(ILocation loc)
        {
            UpdateWithDestination(loc.Latitude, loc.Longitude, loc.Altitude);
        }

        /// <summary>
        /// 根据目的地来更新位置、姿态等状态。
        /// </summary>
        private void UpdateWithDestination(double lat, double lng, float alt)
        {
            // 与目标点之间的距离。
            var distance = this.CalcDistance(lat, lng, alt);

            // 距离已经很近，直接移动到目标点。
            if (distance < _scaledMaxMoveInInterval)
            {
                MoveToPointImmediately(lat, lng, alt);
                return;
            }

            // 在空间中的移动距离。
            var move = _scaledMaxMoveInInterval;

            // 竖直方向的移动距离。
            var altDelta = (float)(move * (alt - Altitude) / distance);
            // 更新高度。
            Altitude += altDelta;

            // 目标点相对于当前位置的方向。
            var direction = this.CalcDirection2D(lat, lng);

            // 更新姿态。
            if (Mode == FlightMode.RTL)
            {
                // 林俊清, 20160126, 目前飞行器只在返航时会旋转机头，面对飞行方向。
                Yaw = (float)direction.RadToDeg();
                Heading = (short)Yaw;
                Roll = 0;
                Pitch = MAX_TILT_IN_FLIGHT;
            }
            else
            {
                var directionDelta = direction - Heading.DegToRad();
                Roll = MAX_TILT_IN_FLIGHT * (float)Math.Sin(directionDelta);
                Pitch = MAX_TILT_IN_FLIGHT * (float)Math.Cos(directionDelta);
            }

            // 水平面上的移动距离。
            var moveInHorizontalPlane = Math.Sqrt(move * move - altDelta * altDelta);
            if (double.IsNaN(moveInHorizontalPlane))
            {
                MoveToPointImmediately(lat, lng, alt);
                return;
            }
            // 更新纬度。
            Longitude += moveInHorizontalPlane * Math.Sin(direction) * GeographyUtils.CalcMetersToLngSpan(Latitude);
            // 更新经度。
            Latitude += moveInHorizontalPlane * Math.Cos(direction) * GeographyUtils.METERS_TO_LAT_SPAN;
        }
    }
}
