using System;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;

namespace LibreHardwareMonitor.Hardware;

internal static class Mutexes
{
    private static Mutex _ecMutex;
    private static Mutex _isaBusMutex;
    private static Mutex _pciBusMutex;
    private static Mutex _razerMutex;
    private static Mutex _usbSensors;

    /// <summary>
    /// Opens the mutexes.
    /// </summary>
    public static void Open()
    {
        _isaBusMutex = CreateOrOpenExistingMutex("Global\\Access_ISABUS.HTP.Method");
        _pciBusMutex = CreateOrOpenExistingMutex("Global\\Access_PCI");
        _ecMutex = CreateOrOpenExistingMutex("Global\\Access_EC");
        _razerMutex = CreateOrOpenExistingMutex("Global\\RazerReadWriteGuardMutex");
        _usbSensors = CreateOrOpenExistingMutex("Global\\Access_USB_Sensors");

        static Mutex CreateOrOpenExistingMutex(string name)
        {
            try
            {
                var worldRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), MutexRights.FullControl, AccessControlType.Allow);
                var mutexSecurity = new MutexSecurity();
                mutexSecurity.AddAccessRule(worldRule);

#if NETFRAMEWORK
                return new Mutex(false, name, out _, mutexSecurity);
#else
                return MutexAcl.Create(false, name, out _, mutexSecurity);
#endif
            }
            catch (UnauthorizedAccessException)
            {
                try
                {
                    return Mutex.OpenExisting(name);
                }
                catch
                {
                    // Ignored.
                }
            }

            return null;
        }
    }

    /// <summary>
    /// Closes the mutexes.
    /// </summary>
    public static void Close()
    {
        _isaBusMutex?.Close();
        _pciBusMutex?.Close();
        _ecMutex?.Close();
        _razerMutex?.Close();
        _usbSensors?.Close();
    }

    public static bool WaitIsaBus(int millisecondsTimeout)
    {
        return WaitMutex(_isaBusMutex, millisecondsTimeout);
    }

    public static void ReleaseIsaBus()
    {
        _isaBusMutex?.ReleaseMutex();
    }

    public static bool WaitPciBus(int millisecondsTimeout)
    {
        return WaitMutex(_pciBusMutex, millisecondsTimeout);
    }

    public static void ReleasePciBus()
    {
        _pciBusMutex?.ReleaseMutex();
    }

    public static bool WaitEc(int millisecondsTimeout)
    {
        return WaitMutex(_ecMutex, millisecondsTimeout);
    }

    public static void ReleaseEc()
    {
        _ecMutex?.ReleaseMutex();
    }

    public static bool WaitRazer(int millisecondsTimeout)
    {
        return WaitMutex(_razerMutex, millisecondsTimeout);
    }

    public static void ReleaseRazer()
    {
        _razerMutex?.ReleaseMutex();
    }

    public static bool WaitUsbSensors(int millisecondsTimeout)
    {
        return WaitMutex(_usbSensors, millisecondsTimeout);
    }

    public static void ReleaseUsbSensors()
    {
        _usbSensors?.ReleaseMutex();
    }

    private static bool WaitMutex(Mutex mutex, int millisecondsTimeout)
    {
        if (mutex == null)
            return true;

        try
        {
            return mutex.WaitOne(millisecondsTimeout, false);
        }
        catch (AbandonedMutexException)
        {
            return true;
        }
        catch (InvalidOperationException)
        {
            return false;
        }
    }
}
