using System;
using System.Data;
using System.Threading.Tasks;
using ANT_Managed_Library;
using JetBrains.Annotations;
using Sensors.ANT;
using Sensors.Polar;
using UniRx;
using UnityEngine;

namespace Sensors
{
    public sealed class BikeSensorData
    {
        #region signleton

        private static readonly Lazy<BikeSensorData>
            lazy =
                new Lazy<BikeSensorData>
                    (() => new BikeSensorData());

        public static BikeSensorData Instance => lazy.Value;

        #endregion

        [CanBeNull] private PowerMeterReceiver powerMeterReceiver;
        [CanBeNull] private SpeedSensorReceiver speedSensorReceiver;
        [CanBeNull] private HrReceiver hrReceiver;
        [CanBeNull] private PolarReceiver polarReceiver;

        public SpeedSensorData? SpeedData => speedSensorReceiver?.SensorData;
        public PowermeterSensorData? PowermeterData => powerMeterReceiver?.SensorData;
        public HrSensorData? HrData => hrReceiver?.SensorData;
        public PolarSensorData? PolarData => polarReceiver?.SensorData;
        [CanBeNull] public IObservable<EcgData> RawEcgData => polarReceiver?.RawEcgData;
        [CanBeNull] public IObservable<AccData> RawAccData => polarReceiver?.RawAccData;

        public PolarSensorConfig? PolarConfig => polarReceiver?.SensorConfig;

        private readonly Subject<object> polarReceiverAvailable = new Subject<object>();
        public Task PolarReceiverAvailable { get; }

        private BikeSensorData()
        {
            PolarReceiverAvailable = polarReceiverAvailable.ToTask();
        }

        public void StartListening(SpeedSensorConfig? speedSensorConfig = null,
            PolarSensorConfig? polarSensorConfig = null, int? powermeterId = null, int? hrAntId = null)
        {
            if (speedSensorConfig != null)
            {
                if(speedSensorReceiver != null)
                {
                    throw new InvalidConstraintException(
                        "BikeSensorData: Already listening to Speed Sensor");
                }
                speedSensorReceiver = new SpeedSensorReceiver(speedSensorConfig.Value);
                try
                {
                    speedSensorReceiver.StartScan();
                }
                catch (ANT_Exception e)
                {
                    Debug.Log($"Could not initialize SpeedSensorReceiver: {e}");
                    speedSensorReceiver = null;
                }
            }

            if (hrAntId != null)
            {
                if(hrReceiver != null)
                {
                    throw new InvalidConstraintException(
                        "BikeSensorData: Already listening to HR Sensor");
                }
                hrReceiver = new HrReceiver(hrAntId.Value);
                try
                {
                    hrReceiver.StartScan();
                }
                catch (ANT_Exception e)
                {
                    Debug.Log($"Could not initialize HR Receiver: {e}");
                    hrReceiver = null;
                }
            }

            if (powermeterId != null)
            {
                if (powerMeterReceiver != null)
                {
                    throw new InvalidConstraintException(
                        "BikeSensorData: Already listening to Power Sensor");
                }
                powerMeterReceiver = new PowerMeterReceiver(powermeterId.Value);
                try
                {
                    powerMeterReceiver.StartScan();
                    
                }
                catch (ANT_Exception e)
                {
                    Debug.Log($"Could not initialize PowerMeter Receiver: {e}");
                    powerMeterReceiver = null;
                }

            }

            if (polarSensorConfig != null)
            {
                if (polarReceiver != null)
                {
                    throw new InvalidConstraintException(
                        "BikeSensorData: Already listening to Polar Sensor");
                }
                polarReceiver = new PolarReceiver(polarSensorConfig.Value);
                polarReceiver.StartListening();
                polarReceiverAvailable.OnNext(null);
                polarReceiverAvailable.OnCompleted();
            }
        
        }

        public void Dispose()
        {
            polarReceiver?.Dispose();
            //TODO: also dispose ANT sensors?
            polarReceiverAvailable.Dispose();

            polarReceiver = null;
            speedSensorReceiver = null;
            powerMeterReceiver = null;
            hrReceiver = null;
        }
    }
}