using System;
using System.IO;

namespace Pilz.IO
{
    public partial class FileLocker : IDisposable
    {
        private FileStream fsLock = null;

        /// <summary>
        /// Defines the file path to the file that should be locked.
        /// </summary>
        public string FilePath { get; private set; }
        /// <summary>
        /// Defines the file path to the lock file that is used to identify the file lock.
        /// </summary>
        public string LockFile { get; private set; }
        /// <summary>
        /// Defines if the file is locked privatly by this instance.
        /// </summary>
        public bool LockedPrivate { get; private set; } = false;

        /// <summary>
        /// Defines if the file is locked by an other instance/program/user.
        /// </summary>
        public bool LockedExternal
        {
            get
            {
                if (LockedPrivate)
                    return false;
                else
                {
                    string lockFile = FilePath + ".lock";
                    bool isLocked = false;

                    if (File.Exists(lockFile))
                    {
                        try
                        {
                            var fs = new FileStream(lockFile, FileMode.Open, FileAccess.Read);
                            fs.Close();
                        }
                        catch (IOException)
                        {
                            isLocked = true;
                        }
                    }

                    return isLocked;
                }
            }
        }

        /// <summary>
        /// Generate a new instance of <see cref="FileLocker"/> and locks the given file automatically.
        /// </summary>
        /// <param name="filePath">The file path to the file that should be locked.</param>
        public FileLocker(string filePath) : this(filePath, true)
        {
        }

        /// <summary>
        /// Generate a new instance of <see cref="FileLocker"/>
        /// </summary>
        /// <param name="filePath">The file path to the file that should be locked.</param>
        /// <param name="autoLock">Defines if the file should be locked automatically right after creating this instance.</param>
        public FileLocker(string filePath, bool autoLock)
        {
            FilePath = filePath;
            LockFile = filePath + ".lock";
            if (autoLock) Lock();
        }

        /// <summary>
        /// Locks the file, if not already locked privatly.
        /// </summary>
        public void Lock()
        {
            if (!LockedPrivate)
            {
                fsLock = new FileStream(LockFile, FileMode.Create, FileAccess.ReadWrite);
                LockedPrivate = true;
            }
        }

        /// <summary>
        /// Unlocks the file, if locked privatly.
        /// </summary>
        public void Unlock()
        {
            if (LockedPrivate)
            {
                fsLock.Close();
                fsLock.Dispose();
                fsLock = null;
                File.Delete(LockFile);
                LockedPrivate = false;
            }
        }

        #region IDisposable
        private bool disposedValue;

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
                Unlock();
        }

        public void Dispose()
        {
            Dispose(true);
            disposedValue = true;
        }
        #endregion
    }
}