using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ANT_Managed_Library;
using UnityEngine;

namespace Assets.Scripts.Devices.Ant
{
    public class AntConnector : IDisposable
    {
        private readonly byte[] ANTPLUS_NETWORK_KEY = new byte[]
            {
                //Insert the ANT+ network key here:
                0xB9, 0xA5, 0x21, 0xFB, 0xBD, 0x72, 0xC3, 0x45
                //Distribution of source code containing the ANT+ Network Key is prohibited. 
                //You may not add the ANT+ Network Key to this source code and republish it. 
                //The ANT+ Network Key is available to ANT+ Adopters. 
                //Please refer to http://thisisant.com to become an ANT+ Adopter and access the key.
            };
        private readonly static object _lock = new object();
        private static ANT_Device _antDevice = null;
        private ANT_Channel searchChannel;
        private readonly List<ANT_Channel> _channels = new List<ANT_Channel>();
        private int ChannelCount
        {
            get
            {
                //if (_antDevice == null) return 0;
                //return _antDevice.getNumChannels();
                return _channels.Count;
            }
        }
        private bool IsBackgroundScanning { get; set; }
        //int searchingDeviceIndex = -1;

        private List<AbstractAntDevice> deviceList = new List<AbstractAntDevice>();

        public bool IsAvailable = false;
        /// <summary>
        /// 设备号,频道编号
        /// </summary>
        public readonly List<UserdChannel> usedChannels = new List<UserdChannel>();

        public readonly List<AbstractAntDevice> discoveredDevices = new List<AbstractAntDevice>();


        private static AntConnector _antConnector;
        public static AntConnector Instance()
        {
            if (_antConnector == null)
            {
                _antConnector = new AntConnector();
            }

            return _antConnector;
        }        

        //private DeviceService deviceService = new DeviceService();
        private AntConnector()
        {
            if (_antDevice == null)
            {
                //ANT_Common.enableDebugLogs();
                //findUsableAntDevice();
            }
            //CheckStatus();

            deviceList.Add(new FitDevice());
            //deviceList.Add(new ds_AntPlus_Power());
            //deviceList.Add(new ds_AntPlus_Cadence());
            //deviceList.Add(new ds_AntPlus_HeartRate());
            //deviceList.Add(new ds_AntPlus_BikeSpdCad());

            var timer = new System.Timers.Timer(1000);
            timer.AutoReset = true;
            timer.Elapsed += Timer_Elapsed;
            timer.Enabled = true;
        }

        private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            CheckStatus();
        }

        public void CheckStatus()
        {
            if (_antDevice != null)
                return;
            //discoveredDevices.Add(new VirtualPowerDevice());
            this.CreateAntDevice();
        }

        private void CreateAntDevice()
        {
            try
            {
                if ((int)ANT_Common.getNumDetectedUSBDevices() == 0)
                    return;
                _antDevice = new ANT_Device();
                if (_antDevice.getNumChannels() <= 4)
                {
                    _antDevice = null;
                    return;
                }
                if (!_antDevice.setNetworkKey(0, ANTPLUS_NETWORK_KEY, 500))
                    throw new ApplicationException("Failed to set network key");
                IsAvailable = true;
                //_antDevice.configureAdvancedBurstSplitting(true);

                _antDevice.deviceResponse += DeviceResponse;
                _antDevice.serialError += new ANT_Device.dSerialErrorHandler(antDevice_serialError);
                for (int i = 1; i < _antDevice.getNumChannels(); i++)
                {
                    var channel = _antDevice.getChannel(i);
                    _channels.Add(channel);
                    channel.channelResponse += new dChannelResponseHandler(DeviceResponse);
                }

                if (IsAvailable)
                {
                    usedChannels.Add(new UserdChannel
                    {
                        DeviceNumber = "scan",
                        DeviceTypeId = "",
                        Index = 0
                    });
                    StartNextSearch();
                }
            }
            catch (Exception ex)
            {
                //ANT_Device.shutdownDeviceInstance(ref _antDevice);    //Don't leave here with an invalid device ref
                //throw new Exception("Could not connect to valid USB2: " + ex.Message);   //forward the exception
                //Log.ErrorLog(ex);
                //Debug.LogError(ex);
                //throw ex;
                Log(ex.Message);
            }
            finally
            {
            }
        }

        private void Log(string str)
        {
            //System.IO.File.AppendAllText(@"D:\work\PowerFun\PowerFun-Unity\Build\log.txt", str+"\r\n");
            Debug.LogError(str);
        }

        private void antDevice_serialError(ANT_Device sender, ANT_Device.serialErrorCode error, bool isCritical)
        {
            //throw new NotImplementedException();
            Log("出错了");
            if (error != ANT_Device.serialErrorCode.DeviceConnectionLost)
                return;

            if (_antDevice != null)
            {
                _antDevice.deviceResponse -= DeviceResponse;
                _antDevice.serialError -= antDevice_serialError;
            }
            _antDevice = null;
            IsAvailable = false;
            foreach (var item in _channels)
            {
                item.channelResponse -= DeviceResponse;
            }
            searchChannel.channelResponse -= antChannel_channelResponse_FeSearch;
            searchChannel = null;
            discoveredDevices.Clear();
            usedChannels.Clear();
            IsBackgroundScanning = false;

        }

        void StartNextSearch()
        {
            //Console.WriteLine("startNextSearch");
            if (searchChannel != null)
            {
                //searchChannel.Dispose();
            }

            //Get new search channel if neccesary
            if (searchChannel == null)
            {
                //if (usedChannels.Count >= ChannelCount)
                //    return;     //no free channels

                //Find the first free channel and start the search
                //for (int i = 0; i < ChannelCount; ++i)
                //{
                //    if (!usedChannels.Values.Contains((byte)i))
                //    {
                //        searchChannel = _antDevice.getChannel(i);
                //        searchChannel.channelResponse += new dChannelResponseHandler(antChannel_channelResponse_FeSearch);
                //        break;
                //    }
                //}
                searchChannel = _antDevice.getChannel(0); // _antDevice.getChannel(usedChannels["scan"]);
                searchChannel.channelResponse += new dChannelResponseHandler(antChannel_channelResponse_FeSearch);
            }
            IsBackgroundScanning = true;
            ReSearch();
        }

        void DeviceResponse(ANT_Response response)
        {
            //Debug.Log(response.responseID);
            var channelIndex = response.messageContents[0];

            var antMessageId = (ANT_ReferenceLibrary.ANTMessageID)response.responseID;
            if (antMessageId == ANT_Managed_Library.ANT_ReferenceLibrary.ANTMessageID.RESPONSE_EVENT_0x40)
            {
                //Trace.WriteLine(string.Join(",", response.messageContents));
                var antEventId = (ANT_ReferenceLibrary.ANTEventID)response.messageContents[2];
                //Trace.WriteLine(antEventId.ToString() +",  " + string.Join(",",response.messageContents));
                if (antEventId == ANT_ReferenceLibrary.ANTEventID.EVENT_CHANNEL_CLOSED_0x07)
                {

                }
                //设备断开连接
                else if (antEventId == ANT_ReferenceLibrary.ANTEventID.EVENT_RX_FAIL_GO_TO_SEARCH_0x08)
                {
                    Debug.Log(antEventId.ToString() + ",  " + string.Join(",", response.messageContents));

                    var cc1 = usedChannels.Where(d => d.Index == channelIndex).FirstOrDefault();
                    var dd1 = discoveredDevices.SingleOrDefault(d => d.searchProfile.deviceNumber.ToString() == cc1.DeviceNumber && d.searchProfile.deviceType.ToString() == cc1.DeviceTypeId);
                    if (dd1 != null)
                    {
                        dd1.Disconnect(false);
                        return;
                    }
                }
            }
            else
            {
                //Trace.WriteLine(antMessageId + ",    " + string.Join(",", response.messageContents));//EVENT_RX_FAIL_GO_TO_SEARCH_0x08                
            }

            //if (antMessageId == ANT_ReferenceLibrary.ANTMessageID.BURST_DATA_0x50)

            if (response.messageContents.Length == 3) return;
            var str = string.Join(",", response.messageContents);

            var cc = usedChannels.Where(d => d.Index == channelIndex).FirstOrDefault();
            var dd = discoveredDevices.SingleOrDefault(d => d.searchProfile.deviceNumber.ToString() == cc.DeviceNumber && d.searchProfile.deviceType.ToString() == cc.DeviceTypeId);
            if (dd != null)
            {
                if (dd.State == DeviceState.Connecting)
                {
                    dd.State = DeviceState.Connected;
                    //IsBackgroundScanning = true;
                    //ReSearch();
                    //StartNextSearch();
                    return;
                }
                dd.handleChannelResponse(response);
            }
        }

        public bool SendMessage(byte msgID, byte[] msgData)
        {
            return _antDevice.writeRawMessageToDevice(msgID, msgData);
        }

        public bool SendMessage(ANTMessage data)
        {
            return SendMessage(data.Id, data.Data);
        }

        void antChannel_channelResponse_FeSearch(ANT_Response response)
        {
            if (IsBackgroundScanning == false)
            {
                searchChannel.closeChannel();
                return;
            }

            if (response.messageContents.Length == 3) return;



            var message = response.messageContents;
            if (response.messageContents.Length >= 14)
            {
                message = response.messageContents.Skip(10).Take(4).ToArray();

                var deviceNumber = message[0] + (message[1] << 8);
                var deviceTypeId = message[2] & 127;
                var transmissionTypeId = message[3];

                //Console.WriteLine($"                                             {deviceNumber}, {deviceTypeId}");

                var device2 = discoveredDevices.FirstOrDefault(d => d.searchProfile.deviceNumber == deviceNumber && d.searchProfile.deviceType == deviceTypeId);
                if (device2 != null)
                {
                    //ReSearch();
                    if (device2.State == DeviceState.Connected)
                    {
                        device2.handleChannelResponse(response);
                        return;
                    }
                    else if (device2.State == DeviceState.Disconnected)
                    {
                        //var deviceInfo = deviceService.Get(PubCommData.CurrentUser.Id, $"{ device2.searchProfile.deviceNumber }:{ device2.searchProfile.deviceType }");
                        //if (deviceInfo != null && deviceInfo.Paired)
                        //{
                        //    device2.Connect();
                        //}
                    }
                    return;
                }
                var device1 = deviceList.SingleOrDefault(d => d.searchProfile.deviceType == deviceTypeId);
                if (device1 != null)
                {
                    AbstractAntDevice device = null;
                    switch (device1.Sensor)
                    {
                        case SensorType.None:
                            break;
                        case SensorType.Cadence:
                            //device = new ds_AntPlus_Cadence();
                            break;
                        case SensorType.HeartRate:
                            //device = new ds_AntPlus_HeartRate();
                            break;
                        case SensorType.Power:
                            //device = new ds_AntPlus_Power();
                            break;
                        case SensorType.Speed:
                            break;
                        case SensorType.SpeedCadence:
                            //device = new ds_AntPlus_BikeSpdCad();
                            break;
                        case SensorType.Trainer:
                            device = new FitDevice();
                            break;
                        case SensorType.VirtualPower:
                            break;
                        default:
                            break;
                    }
                    if (device != null)
                    {
                        //device.searchProfile.deviceNumber = (ushort)deviceNumber;
                        device.DeviceNumber = (ushort)deviceNumber;
                        discoveredDevices.Add(device);


                    }
                    //ReSearch();
                    return;
                }
            }
        }

        private void ReSearch()
        {
            if (IsBackgroundScanning == false) return;
            //Console.WriteLine($"research,{ DateTime.Now }");
            if (searchChannel != null)
            {
                //searchChannel.closeChannel(500);
                //searchChannel.unassignChannel();
                //searchChannel.openChannel();
                //return;
            }
            var channelIndex = (byte)0; //searchChannel.getChannelNum();
                                        //Handle setting the search timeout
            byte timeout = 4; //default 4*2.5=10 seconds for each device
                              //if (deviceList.Count - usedChannels.Count == 1)
            timeout = 255;  //search forever if we only have one device to find; If one of the other devices resets it will startNextSearch again so we won't get stuck

            searchChannel.assignChannelExt(ANT_ReferenceLibrary.ChannelType.ADV_TxRx_Only_or_RxAlwaysWildCard_0x40, 0, ANT_ReferenceLibrary.ChannelTypeExtended.ADV_AlwaysSearch_0x01);
            //SendMessage((int)ANT_ReferenceLibrary.ANTMessageID.ASSIGN_CHANNEL_0x42, new byte[] {
            //        channelIndex,
            //        (int)ANT_ReferenceLibrary.ChannelType.ADV_TxRx_Only_or_RxAlwaysWildCard_0x40,
            //        0
            //    });
            searchChannel.setChannelID(0, false, 0, 0);
            searchChannel.setChannelFreq(57);
            SendMessage(102, new byte[] { channelIndex, Convert.ToByte(true) });
            searchChannel.setLowPrioritySearchTimeout(timeout);
            searchChannel.setChannelSearchTimeout(0);
            searchChannel.openChannel(500);
        }

        public void ConnectDevice(AbstractAntDevice device)
        {
            if (usedChannels.Any(u => u.DeviceNumber == device.searchProfile.deviceNumber.ToString() &&
                u.DeviceTypeId == device.searchProfile.deviceType.ToString()))
                return;

            if (device.Sensor == SensorType.Trainer || device.Sensor == SensorType.Power)
            {
                if (discoveredDevices.Any(s => s.Sensor == device.Sensor && (s.State == DeviceState.Connected || s.State == DeviceState.Connecting)))
                {
                    return;
                }
            }
            if (device.Sensor == SensorType.Cadence)
            {
                if (discoveredDevices.Any(s => s.Sensor == device.Sensor && (s.State == DeviceState.Connected || s.State == DeviceState.Connecting)))
                {
                    return;
                }
            }
            if (device.Sensor == SensorType.HeartRate)
            {
                if (discoveredDevices.Any(s => s.Sensor == device.Sensor && (s.State == DeviceState.Connected || s.State == DeviceState.Connecting)))
                {
                    return;
                }
            }

            for (int i = 0; i < ChannelCount; i++)
            {
                if (!usedChannels.Any(u => u.Index == (byte)i))
                {
                    usedChannels.Add(new UserdChannel
                    {
                        DeviceNumber = device.searchProfile.deviceNumber.ToString(),
                        DeviceTypeId = device.searchProfile.deviceType.ToString(),
                        Index = (byte)i
                    });
                    break;
                }
            }

            //var deviceService = new DeviceService();
            //var info = deviceService.Get(PubCommData.CurrentUser.Id, device.searchProfile.deviceNumber.ToString() + ":" + device.searchProfile.deviceType);
            //if (info != null && info.Paired == false)
            //{
            //    info.Paired = true;
            //    deviceService.Update(info);
            //}

            #region 配对
            device.State = DeviceState.Connecting;

            //var r = searchChannel.closeChannel(500);
            //if (!r)
            //{
            //    throw new Exception("关闭搜索频道出错");
            //}

            //CloseChannel(searchChannel);
            //IsBackgroundScanning = false;

            var channelIndex = usedChannels.Single(c => c.DeviceNumber == device.searchProfile.deviceNumber.ToString() && c.DeviceTypeId == device.searchProfile.deviceType.ToString()).Index;

            var channel = _antDevice.getChannel(channelIndex);
            //channel.channelResponse += DeviceResponse;
            channel.assignChannel(ANT_ReferenceLibrary.ChannelType.BASE_Slave_Receive_0x00, 0);
            channel.setChannelID(device.searchProfile.deviceNumber, false, (byte)device.searchProfile.deviceType, device.searchProfile.transType);
            channel.setChannelPeriod(device.searchProfile.messagePeriod);
            channel.setChannelFreq(device.searchProfile.rfOffset);
            channel.openChannel();

            //if (!r1)
            //{
            //    throw new Exception($"打开配对频道出错{ channelIndex }");
            //}
            //Console.WriteLine($"                        连接设备{ device.searchProfile.deviceNumber }, 频道{ channelIndex },{ DateTime.Now }");
            

            //Task.Delay(2000).ContinueWith((t) =>
            //{
            //    IsBackgroundScanning = true;
            //    ReSearch();
            //});
            #endregion
        }

        public void DisconnectDevice(AbstractAntDevice device, bool save)
        {
            var deviceNumber = device.searchProfile.deviceNumber.ToString();
            var deviceType = device.searchProfile.deviceType.ToString();
            var item = usedChannels.SingleOrDefault(u => u.DeviceNumber == deviceNumber && u.DeviceTypeId == deviceType);
            if (item == null)
                return;
            device.State = DeviceState.Disconnecting;
            var channelIndex = item.Index;
            var channel = _antDevice.getChannel(channelIndex);
            CloseChannel(channel);
            usedChannels.Remove(item);
            device.State = DeviceState.Disconnected;

            if (save)
            {
                //var deviceService = new DeviceService();
                //var info = deviceService.Get(PubCommData.CurrentUser.Id, device.searchProfile.deviceNumber.ToString() + ":" + device.searchProfile.deviceType);
                //if (info != null && info.Paired)
                //{
                //    info.Paired = false;
                //    deviceService.Update(info);
                //}
            }
        }

        public void Dispose()
        {
            discoveredDevices.Clear();
            deviceList.Clear();

            _channels.Clear();

            if (_antDevice != null)
            {
                _antDevice.Dispose();
            }
        }

        public void CloseChannel(ANT_Channel channel)
        {
            channel.closeChannel(500);
            channel.unassignChannel(500);
        }
    }
}
