/* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
 * 
 * You can redistribute this program and/or modify it under the terms of
 * the GNU Lesser Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Win32.SafeHandles;
using Utilities;
using DiskAccessLibrary.LogicalDiskManager;

namespace DiskAccessLibrary
{
    public class WindowsVolumeManager
    {
        public static bool ExclusiveLock(Guid windowsVolumeGuid)
        {
            return ExclusiveLock(windowsVolumeGuid, FileAccess.ReadWrite);
        }

        /// <summary>
        /// Windows will flush all cached data to the volume before locking it.
        /// Note: we can only lock a dynamic volume if the disk is online and the volume is operational.
        /// </summary>
        /// <returns>True if a new lock has been obtained</returns>
        public static bool ExclusiveLock(Guid windowsVolumeGuid, FileAccess fileAccess)
        {
            bool newAllocation;
            // Windows Vista / 7: Valid handle cannot be obtained for a dynamic volume using ShareMode.ReadWrite.
            // The FSCTL_LOCK_VOLUME documentation demands ShareMode.ReadWrite, but ShareMode.None or ShareMode.Write will work too.
            SafeFileHandle handle = VolumeHandlePool.ObtainHandle(windowsVolumeGuid, fileAccess, ShareMode.None, out newAllocation);
            if (newAllocation)
            {
                if (!handle.IsInvalid)
                {
                    bool success = VolumeControl.LockVolume(handle);
                    if (!success)
                    {
                        VolumeHandlePool.ReleaseHandle(windowsVolumeGuid);
                    }
                    return success;
                }
                else
                {
                    VolumeHandlePool.ReleaseHandle(windowsVolumeGuid);
                    return false;
                }
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Note that the volume will be remounted again on first access.
        /// </summary>
        public static bool DismountVolume(Guid windowsVolumeGuid)
        {
            bool releaseHandle;
            SafeFileHandle handle = VolumeHandlePool.ObtainHandle(windowsVolumeGuid, FileAccess.ReadWrite, ShareMode.ReadWrite, out releaseHandle);
            bool success = false;
            if (!handle.IsInvalid)
            {
                success = VolumeControl.DismountVolume(handle);
            }

            if (releaseHandle) // new allocation
            {
                VolumeHandlePool.ReleaseHandle(windowsVolumeGuid);
            }
            return success;
        }


        public static bool ReleaseLock(Guid windowsVolumeGuid)
        {
            return VolumeHandlePool.ReleaseHandle(windowsVolumeGuid);
        }

        /// <summary>
        /// If Windows Automount is turned off, then partitions on new disks are not mounted. (but can be locked).
        /// Failed dynamic volumes are not mounted as well. (and can't be locked).
        /// Note: A volume can have 0 mount points and still be mounted and access by Windows.
        /// Note: The NTFS file system treats a locked volume as a dismounted volume.
        /// </summary>
        public static bool IsMounted(Guid windowsVolumeGuid)
        {
            return VolumeControl.IsVolumeMounted(windowsVolumeGuid);
        }

        public static List<string> GetMountPoints(Guid windowsVolumeGuid)
        {
            return VolumeControl.GetVolumeMountPoints(windowsVolumeGuid);
        }

        /// <summary>
        /// A volume can be mounted by the OS even if it has no mount points
        /// </summary>
        public static bool HasMountPoints(Guid windowsVolumeGuid)
        {
            return (GetMountPoints(windowsVolumeGuid).Count > 0);
        }
    }
}