using MediaBrowser.Common.IO;
using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.IsoMounter
{
    /// 
    /// Class IsoManager
    /// 
    public class PismoIsoManager : IIsoManager
    {
        /// 
        /// The mount semaphore - limit to four at a time.
        /// 
        private readonly SemaphoreSlim _mountSemaphore = new SemaphoreSlim(4,4);
        /// 
        /// The PFM API
        /// 
        private PfmApi _pfmApi;
        /// 
        /// The _PFM API initialized
        /// 
        private bool _pfmApiInitialized;
        /// 
        /// The _PFM API sync lock
        /// 
        private object _pfmApiSyncLock = new object();
        /// 
        /// Gets the display prefs.
        /// 
        /// The display prefs.
        private PfmApi PfmApi
        {
            get
            {
                LazyInitializer.EnsureInitialized(ref _pfmApi, ref _pfmApiInitialized, ref _pfmApiSyncLock, () =>
                {
                    var err = PfmStatic.InstallCheck();
                    if (err != PfmInst.installed)
                    {
                        throw new Exception("Pismo File Mount Audit Package is not installed");
                    } 
                    
                    PfmApi pfmApi;
                    err = PfmStatic.ApiFactory(out pfmApi);
                    if (err != 0)
                    {
                        throw new IOException("Unable to open PFM Api. Pismo File Mount Audit Package is probably not installed.");
                    }
                    return pfmApi;
                });
                return _pfmApi;
            }
        }
        /// 
        /// The _has initialized
        /// 
        private bool _hasInitialized;
        /// 
        /// Gets or sets the logger.
        /// 
        /// The logger.
        private ILogger Logger { get; set; }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The logger.
        public PismoIsoManager(ILogger logger)
        {
            Logger = logger;
            _myPfmFileMountUi = new MyPfmFileMountUi(Logger);
        }
        /// 
        /// The _my PFM file mount UI
        /// 
        private readonly MyPfmFileMountUi _myPfmFileMountUi;
        /// 
        /// Mounts the specified iso path.
        /// 
        /// The iso path.
        /// The cancellation token.
        /// if set to true [visible to all processes].
        /// IsoMount.
        /// isoPath
        /// Unable to create mount.
        public async Task Mount(string isoPath, CancellationToken cancellationToken, bool visibleToAllProcesses = true)
        {
            if (string.IsNullOrEmpty(isoPath))
            {
                throw new ArgumentNullException("isoPath");
            }
            PfmFileMount mount;
            var err = PfmApi.FileMountCreate(out mount);
            if (err != 0)
            {
                throw new IOException("Unable to create mount for " + isoPath);
            }
            _hasInitialized = true;
            var fmp = new PfmFileMountCreateParams { };
            fmp.ui = _myPfmFileMountUi;
            fmp.fileMountFlags |= PfmFileMountFlag.inProcess;
            if (visibleToAllProcesses)
            {
                fmp.visibleProcessId = PfmVisibleProcessId.all;
            }
            fmp.mountFileName = isoPath;
            // unc only
            fmp.mountFlags |= PfmMountFlag.uncOnly;
            fmp.mountFlags |= PfmMountFlag.noShellNotify;
            fmp.mountFlags |= PfmMountFlag.readOnly;
            Logger.Info("Mounting {0}", isoPath);
            await _mountSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
            err = mount.Start(fmp);
            if (err != 0)
            {
                _mountSemaphore.Release();
                mount.Dispose();
                throw new IOException("Unable to start mount for " + isoPath);
            }
            err = mount.WaitReady();
            if (err != 0)
            {
                _mountSemaphore.Release();
                mount.Dispose();
                throw new IOException("Unable to start mount for " + isoPath);
            }
            return new PismoMount(mount, isoPath, this, Logger);
        }
        /// 
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// 
        public void Dispose()
        {
            Dispose(true);
        }
        /// 
        /// Releases unmanaged and - optionally - managed resources.
        /// 
        /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
        protected virtual void Dispose(bool dispose)
        {
            if (dispose)
            {
                if (_hasInitialized)
                {
                    Logger.Info("Disposing PfmPapi");
                    _pfmApi.Dispose();
                    Logger.Info("PfmStatic.ApiUnload");
                    PfmStatic.ApiUnload();
                }
            }
        }
        /// 
        /// Gets a value indicating whether this instance can mount.
        /// 
        /// The path.
        /// true if this instance can mount the specified path; otherwise, false.
        /// true if this instance can mount; otherwise, false.
        public bool CanMount(string path)
        {
            try
            {
                return string.Equals(Path.GetExtension(path), ".iso", StringComparison.OrdinalIgnoreCase) && PfmApi != null;
            }
            catch
            {
                return false;
            }
        }
        /// 
        /// Called when [unmount].
        /// 
        /// The mount.
        internal void OnUnmount(PismoMount mount)
        {
            _mountSemaphore.Release();
        }
    }
}