/* Copyright (c) Citrix Systems, Inc. 
 * All rights reserved. 
 * 
 * Redistribution and use in source and binary forms, 
 * with or without modification, are permitted provided 
 * that the following conditions are met: 
 * 
 * *   Redistributions of source code must retain the above 
 *     copyright notice, this list of conditions and the 
 *     following disclaimer. 
 * *   Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the 
 *     following disclaimer in the documentation and/or other 
 *     materials provided with the distribution. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XenAdmin;
using XenAdmin.Core;

namespace XenAPI
{
    public partial class VDI : IComparable<VDI>, IEquatable<VDI>
    {
        public override string Name()
        {
            if (Connection != null)
            {
                SR sr = Connection.Resolve(this.SR);
                if (sr != null)
                {
                    Host host = sr.GetStorageHost();
                    if (sr.Physical() && host != null)
                        return string.Format(Messages.CD_DRIVE, host.Name());

                }
            }
            return name_label;
        }

        public override string Description()
        {
            return name_description;
        }

        public string VMsOfVDI()
        {

            StringBuilder sb = new StringBuilder();
            bool comma = false;

            foreach (VM vm in this.GetVMs().Where(vm => vm.Show(true)))
            {
                if (comma)
                    sb.Append(", ");
                else
                    comma = true;

                sb.Append(vm.is_a_snapshot ? String.Format(Messages.SNAPSHOT_BRACKETS, vm.Name()) : vm.Name());
            }

            return sb.ToString();
        }

        public override bool Show(bool showHiddenVMs)
        {
            return !missing && managed && (showHiddenVMs || !IsHidden());
        }

        public override bool IsHidden()
        {
            return BoolKey(other_config, HIDE_FROM_XENCENTER);
        }

        public IList<VM> GetVMs()
        {
            List<VM> vms = new List<VM>();

            if (this.type == vdi_type.crashdump)
            {
                foreach (Crashdump crashdump in Connection.ResolveAll<Crashdump>(crash_dumps))
                {
                    VM vm = Connection.Resolve<VM>(crashdump.VM);

                    if (vm != null)
                        vms.Add(vm);
                }
            }
            else if (this.type == vdi_type.suspend)
            {
                foreach (VM vm in Connection.Cache.VMs)
                {
                    VDI vdi = Connection.Resolve<VDI>(vm.suspend_VDI);

                    if (vdi != null && vdi.uuid == this.uuid)
                        vms.Add(vm);
                }
            }
            else
            {
                foreach (VBD vbd in Connection.ResolveAll(this.VBDs))
                {
                    VM vm = Connection.Resolve(vbd.VM);
                    if (vm != null)
                        vms.Add(vm);
                }
            }

            vms.Sort();

            return vms;
        }

        public string GetVmHint()
        {
            if (sm_config != null && sm_config.ContainsKey("vmhint"))
                return sm_config["vmhint"];
            else
                return "";
        }

        public void SetVmHint(string value)
        {
            sm_config = SetDictionaryKey(sm_config, "vmhint", value);
        }

        public override string ToString()
        {
            return Name();
        }

        public virtual string SizeText()
        {
            if (is_a_snapshot)
                return String.Empty;
            else
                return Util.DiskSizeString(virtual_size);
        }

        public override string NameWithLocation()
        {
            if (Connection != null)
            {
                var srOfVdi = Connection.Resolve(SR);
                if (srOfVdi == null)
                    return base.NameWithLocation();

                return string.Format(Messages.VDI_ON_SR_TITLE, Name(), srOfVdi.Name(), srOfVdi.LocationString());
            }

            return base.NameWithLocation();
        }

        #region IEquatable<VDI> Members

        /// <summary>
        /// Indicates whether the current object is equal to the specified object. This calls the implementation from XenObject.
        /// This implementation is required for ToStringWrapper.
        /// </summary>
        public bool Equals(VDI other)
        {
            return base.Equals(other);
        }

        #endregion

        /// <summary>
        /// These are some types of VDI we care to distinguish between in the GUI. Determining what type a VDI is for XC depends on not
        /// just the vdi.type fields supplied by the server but some other logic. This is contained in VDI.VDIType, which returns one of
        /// these enums.
        /// </summary>
        public enum FriendlyType { SNAPSHOT, ISO, SYSTEM_DISK, VIRTUAL_DISK, NONE };

        /// <summary>
        /// These are some types of VDI we care to distinguish between in the GUI. Determining what type a VDI is for XC depends on not
        /// just the vdi.type fields supplied by the server but some other logic. 
        /// </summary>
        public FriendlyType VDIType()
        {
            SR sr = Connection.Resolve<SR>(SR);
            if (sr == null)
                return FriendlyType.NONE;

            if (is_a_snapshot && GetVMs().Count >= 1)
                return FriendlyType.SNAPSHOT;

            if (sr.content_type == XenAPI.SR.Content_Type_ISO)
                return FriendlyType.ISO;

            if (type == vdi_type.system)
                return FriendlyType.SYSTEM_DISK;

            return FriendlyType.VIRTUAL_DISK;
        }

        /// <summary>
        /// Is the disk of a type used by HA?
        /// </summary>
        public bool IsHaType()
        {
            return type == vdi_type.ha_statefile || type == vdi_type.redo_log;
        }

        /// <summary>
        /// Is the disk currently being used by HA?
        /// </summary>
        public bool IsUsedByHA()
        {
            return IsHaType() && Helpers.GetPoolOfOne(Connection).ha_enabled;
        }

        /// <summary>
        /// Is the disk used by DR?
        /// </summary>
        public bool IsMetadataForDR()
        {
            return type == vdi_type.metadata;
        }

        /// <summary>
        /// VDIs associated with storage motion are specially marked. This
        /// bool allows you to know if the current VDI is such.
        /// </summary>
        public bool IsAnIntermediateStorageMotionSnapshot()
        {
            return sm_config.ContainsKey("base_mirror");
        }


        /// <summary>
        /// Try to determine if this VDI belongs to a WSS VM - this is a best guess only
        /// </summary>
        public bool CouldBeWss()
        {
            const string wssName = "Webss-disk";
            return name_label.Contains(wssName);
        }

        public bool IsCloudConfigDrive()
        {
            return other_config.ContainsKey("config-drive") && other_config["config-drive"].ToLower() == "true";
        }

        /// <summary>
        /// Whether this is a Tools ISO.
        /// The new method is to check the is_tools_iso flag, the old one to check the name_label.
        /// </summary>
        public bool IsToolsIso()
        {
            string[] toolIsoNames = {"xswindrivers.iso", "xs-tools.iso", "guest-tools.iso"};
            return is_tools_iso || toolIsoNames.Contains(name_label);
        }

        /// <summary>
        /// Whether read caching is enabled on this disk on a specific host
        /// </summary>
        public bool ReadCachingEnabled(Host host)
        {
            return BoolKey(sm_config, "read-caching-enabled-on-" + host.uuid);
        }

        /// <summary>
        /// ... and if not, why not
        /// </summary>
        public ReadCachingDisabledReasonCode ReadCachingDisabledReason(Host host)
        {
            string reasonstr;
            if (sm_config.TryGetValue("read-caching-reason-" + host.uuid, out reasonstr))
            {
                ReadCachingDisabledReasonCode reason;
                if (Enum.TryParse(reasonstr, out reason))
                    return reason;
            }
            return ReadCachingDisabledReasonCode.UNKNOWN;
        }

        /// <summary>
        /// Possible reasons for read caching being disabled
        /// If there are multiple reasons, the topmost reason will be returned
        /// </summary>
        public enum ReadCachingDisabledReasonCode
        {
            UNKNOWN,  // catch-all, shouldn't occur if read caching is disabled
            LICENSE_RESTRICTION,  // self-explanatory
            SR_NOT_SUPPORTED,  // the SR is not NFS or EXT
            NO_RO_IMAGE,  // no part of the VDI is read-only => nothing to cache
            SR_OVERRIDE,  // the feature has been explicitly disabled for the VDI's SR
        }

    }
}
