using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Teradyne.Instruments.HighSpeed.SubsystemDriver;

namespace Raytheon.GuidedElectronicsUnit
{
    internal static class HardwareSessionFactory
    {
        //private static readonly Dictionary<MindWorks.Nimbus.Driver, HardwareSession> _driverSessionToHardwareSession =
        //    new();
        private static readonly Dictionary<string, HardwareSession> _resourceNameToHardwareSession = new();
        private static readonly Dictionary<HardwareSession, List<MindWorks.Nimbus.Driver>> _hardwareSessionToDriverSession = new();

        private static readonly object _componentLock = new();

        public static T NewHardwareSession<T>(
            MindWorks.Nimbus.Driver driverSession,
            string resourceName,
            bool simulated,
            bool reset) where T : HardwareSession, new()
        {
            lock (_componentLock)
            {
                HardwareSession foundHardwareSession = null;
                terHsi terHsiSession = null;

                //if (_driverSessionToHardwareSession.ContainsKey(driverSession))
                //    return _driverSessionToHardwareSession[driverSession] as T;
                if (_resourceNameToHardwareSession.ContainsKey(resourceName))
                    return _resourceNameToHardwareSession[resourceName] as T;

                if (simulated)
                {
                    terHsiSession = terHsi.CreateSession(resourceName, reset, "simulate=true");
                }
                else
                {
                    // Call the driver's init function to get a session handle.
                    terHsiSession = terHsi.CreateSession(resourceName, reset);
                    driverSession.Session.InstrumentFirmwareRevision = terHsiSession.FirmwareRevision;
                    driverSession.Session.InstrumentManufacturer = terHsiSession.Manufacturer;
                }


                // If the returned session handle is new, then create a wrapper for it.
                foreach (var entry in _hardwareSessionToDriverSession)
                {
                    HardwareSession hardwareSession = entry.Key;
                    if (hardwareSession.TerHsiSession == terHsiSession)
                    {
                        entry.Value.Add(driverSession);
                        foundHardwareSession = hardwareSession;
                        break;
                    }
                }

                if (foundHardwareSession == null)
                {
                    var driverSessionList = new List<MindWorks.Nimbus.Driver>();

                    // Create a hardware session and associate the driver session with it.
                    foundHardwareSession = new T
                    {
                        TerHsiSession = terHsiSession,
                        IsSimulated = simulated
                    };
                    driverSessionList.Add(driverSession);
                    _hardwareSessionToDriverSession.Add(foundHardwareSession, driverSessionList);

                    foundHardwareSession.InitSession();
                }

                // Record the hardware session associated with the driver session and
                // return the hardware session.
                //_driverSessionToHardwareSession.Add(driverSession, foundHardwareSession);
                _resourceNameToHardwareSession.Add(resourceName, foundHardwareSession);

                return foundHardwareSession as T;
            }
        }

        public static void EndHardwareSession(MindWorks.Nimbus.Driver driverSession, string resourceName)
        {
            lock (_componentLock)
            {
                // Locate the hardware session associated with the driver session.
                //if (!_driverSessionToHardwareSession.TryGetValue(driverSession, out HardwareSession hardwareSession))
                if (!_resourceNameToHardwareSession.TryGetValue(resourceName, out HardwareSession hardwareSession))
                {
                    // Not found, nothing to do
                    return;
                }

                if (!_hardwareSessionToDriverSession.TryGetValue(hardwareSession, out List<MindWorks.Nimbus.Driver> driverSessionList))
                {
                    // Not found, nothing to do
                    return;
                }

                // Remove the driver session from the list of driver sessions supported by the hardware session.
                if (!driverSessionList.Remove(driverSession))
                {
                    // Nothing found, nothing to do
                    return;
                }

                // If there are no more sessions for this wrapper, then shutdown the wrapper
                // and delete it from our list.
                if (driverSessionList.Count == 0)
                {
                    _hardwareSessionToDriverSession.Remove(hardwareSession);

                    // Close the hardware session too
                    terHsi terHsiSession = hardwareSession.TerHsiSession;
                    terHsiSession?.Reset();
                    terHsiSession?.Dispose();

                    hardwareSession = null;
                }

                // Remove the driver session entry in our list of driver sessions.
                //_driverSessionToHardwareSession.Remove(driverSession);
                _resourceNameToHardwareSession.Remove(resourceName);
            }
        }
    }
}
