//-----------------------------------------------------------------------
// 
//     Copyright (c) Microsoft Corporation.
// 
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using Microsoft.Isam.Esent.Interop.Vista;
    /// 
    /// Contains the information needed to create an index over data in an ESE database.
    /// 
    [SuppressMessage(
        "Microsoft.StyleCop.CSharp.NamingRules",
        "SA1300:ElementMustBeginWithUpperCaseLetter",
        Justification = "This should match the unmanaged API, which isn't capitalized.")]
    [Serializable]
    public sealed partial class JET_INDEXCREATE : IContentEquatable, IDeepCloneable
    {
        /// 
        /// Name of the index.
        /// 
        private string name;
        /// 
        /// Index key.
        /// 
        private string key;
        /// 
        /// Length of the index key.
        /// 
        private int keyLength;
        /// 
        /// Index options.
        /// 
        private CreateIndexGrbit options;
        /// 
        /// Index density.
        /// 
        private int density;
        /// 
        /// Unicode comparison options.
        /// 
        private JET_UNICODEINDEX unicodeOptions;
        /// 
        /// Maximum length of a column to store in the index.
        /// 
        private int maxSegmentLength;
        /// 
        /// Conditional columns.
        /// 
        private JET_CONDITIONALCOLUMN[] conditionalColumns;
        /// 
        /// Number of conditional columns.
        /// 
        private int numConditionalColumns;
        /// 
        /// Error code from index creation.
        /// 
        private JET_err errorCode;
        /// 
        /// Maximum length of index keys.
        /// 
        private int maximumKeyLength;
        /// 
        /// Space allocation, maintenance, and usage hints.
        /// 
        private JET_SPACEHINTS spaceHints;
        /// 
        /// Gets or sets the error code from creating this index.
        /// 
        public JET_err err
        {
            [DebuggerStepThrough]
            get { return this.errorCode; }
            set { this.errorCode = value; }
        }
        /// 
        /// Gets or sets the name of the index to create. 
        /// 
        public string szIndexName
        {
            [DebuggerStepThrough]
            get { return this.name; }
            set { this.name = value; }
        }
        /// 
        /// Gets or sets the description of the index key. This is a double 
        /// null-terminated string of null-delimited tokens. Each token is
        /// of the form [direction-specifier][column-name], where
        /// direction-specification is either "+" or "-". for example, a
        /// szKey of "+abc\0-def\0+ghi\0" will index over the three columns
        /// "abc" (in ascending order), "def" (in descending order), and "ghi"
        /// (in ascending order).
        /// 
        public string szKey
        {
            [DebuggerStepThrough]
            get { return this.key; }
            set { this.key = value; }
        }
        /// 
        /// Gets or sets the length, in characters, of szKey including the two terminating nulls.
        /// 
        public int cbKey
        {
            [DebuggerStepThrough]
            get { return this.keyLength; }
            set { this.keyLength = value; }
        }
        /// 
        /// Gets or sets index creation options.
        /// 
        public CreateIndexGrbit grbit
        {
            [DebuggerStepThrough]
            get { return this.options; }
            set { this.options = value; }
        }
        /// 
        /// Gets or sets the density of the index.
        /// 
        public int ulDensity
        {
            [DebuggerStepThrough]
            get { return this.density; }
            set { this.density = value; }
        }
        /// 
        /// Gets or sets the optional unicode comparison options.
        /// 
        public JET_UNICODEINDEX pidxUnicode
        {
            [DebuggerStepThrough]
            get { return this.unicodeOptions; }
            set { this.unicodeOptions = value; }
        }
        /// 
        /// Gets or sets the maximum length, in bytes, of each column to store in the index.
        /// 
        public int cbVarSegMac
        {
            [DebuggerStepThrough]
            get { return this.maxSegmentLength; }
            set { this.maxSegmentLength = value; }
        }
        /// 
        /// Gets or sets the optional conditional columns.
        /// 
        public JET_CONDITIONALCOLUMN[] rgconditionalcolumn
        {
            [DebuggerStepThrough]
            get { return this.conditionalColumns; }
            set { this.conditionalColumns = value; }
        }
        /// 
        /// Gets or sets the number of conditional columns.
        /// 
        public int cConditionalColumn
        {
            [DebuggerStepThrough]
            get { return this.numConditionalColumns; }
            set { this.numConditionalColumns = value; }
        }
        /// 
        /// Gets or sets the maximum allowable size, in bytes, for keys in the index.
        /// The minimum supported maximum key size is JET_cbKeyMostMin (255) which
        /// is the legacy maximum key size. The maximum key size is dependent on
        /// the database page size . The
        /// maximum key size can be retrieved with .
        /// 
        /// This parameter is ignored on Windows XP and Windows Server 2003.
        /// 
        /// 
        /// Unlike the unmanaged API, 
        /// (JET_bitIndexKeyMost) is not needed, it will be added automatically.
        /// 
        /// 
        public int cbKeyMost
        {
            [DebuggerStepThrough]
            get { return this.maximumKeyLength; }
            set { this.maximumKeyLength = value; }
        }
        /// 
        /// Gets or sets space allocation, maintenance, and usage hints.
        /// 
        public JET_SPACEHINTS pSpaceHints
        {
            [DebuggerStepThrough]
            get { return this.spaceHints; }
            set { this.spaceHints = value; }
        }
        /// 
        /// Returns a deep copy of the object.
        /// 
        /// A deep copy of the object.
        public JET_INDEXCREATE DeepClone()
        {
            JET_INDEXCREATE result = (JET_INDEXCREATE)this.MemberwiseClone();
            result.pidxUnicode = (null == this.pidxUnicode) ? null : this.pidxUnicode.DeepClone();
            this.conditionalColumns = Util.DeepCloneArray(this.conditionalColumns);
            return result;
        }
        /// 
        /// Generate a string representation of the instance.
        /// 
        /// The structure as a string.
        public override string ToString()
        {
            return string.Format(CultureInfo.InvariantCulture, "JET_INDEXCREATE({0}:{1})", this.szIndexName, this.szKey);
        }
        /// 
        /// Returns a value indicating whether this instance is equal
        /// to another instance.
        /// 
        /// An instance to compare with this instance.
        /// True if the two instances are equal.
        public bool ContentEquals(JET_INDEXCREATE other)
        {
            if (null == other)
            {
                return false;
            }
            this.CheckMembersAreValid();
            other.CheckMembersAreValid();
            return this.err == other.err
                            && this.szIndexName == other.szIndexName
                            && this.szKey == other.szKey
                            && this.cbKey == other.cbKey
                            && this.grbit == other.grbit
                            && this.ulDensity == other.ulDensity
                            && this.cbVarSegMac == other.cbVarSegMac
                            && this.cbKeyMost == other.cbKeyMost
                            && this.IsUnicodeIndexEqual(other)
                            && this.AreConditionalColumnsEqual(other);
        }
        /// 
        /// Check this object to make sure its parameters are valid.
        /// 
        internal void CheckMembersAreValid()
        {
            if (null == this.szIndexName)
            {
                throw new ArgumentNullException("szIndexName");
            }
            if (null == this.szKey)
            {
                throw new ArgumentNullException("szKey");
            }
            if (this.cbKey > checked(this.szKey.Length + 1))
            {
                throw new ArgumentOutOfRangeException("cbKey", this.cbKey, "cannot be greater than the length of szKey");
            }
            if (this.cbKey < 0)
            {
                throw new ArgumentOutOfRangeException("cbKey", this.cbKey, "cannot be negative");
            }
            if (this.ulDensity < 0)
            {
                throw new ArgumentOutOfRangeException("ulDensity", this.ulDensity, "cannot be negative");
            }
            if (this.cbKeyMost < 0)
            {
                throw new ArgumentOutOfRangeException("cbKeyMost", this.cbKeyMost, "cannot be negative");
            }
            if (this.cbVarSegMac < 0)
            {
                throw new ArgumentOutOfRangeException("cbVarSegMac", this.cbVarSegMac, "cannot be negative");
            }
            if ((this.cConditionalColumn > 0 && null == this.rgconditionalcolumn)
                || (this.cConditionalColumn > 0 && this.cConditionalColumn > this.rgconditionalcolumn.Length))
            {
                throw new ArgumentOutOfRangeException("cConditionalColumn", this.cConditionalColumn, "cannot be greater than the length of rgconditionalcolumn");
            }
            if (this.cConditionalColumn < 0)
            {
                throw new ArgumentOutOfRangeException(
                    "cConditionalColumn", this.cConditionalColumn, "cannot be negative");
            }
        }
#if !MANAGEDESENT_ON_WSA // Not exposed in MSDK
        /// 
        /// Gets the native (interop) version of this object, except for
        ///  and .
        /// 
        /// The native (interop) version of this object.
        internal NATIVE_INDEXCREATE GetNativeIndexcreate()
        {
            this.CheckMembersAreValid();
            var native = new NATIVE_INDEXCREATE();
            native.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_INDEXCREATE)));
            // szIndexName and szKey are converted at pinvoke time.
            //
            // native.szIndexName = this.szIndexName;
            // native.szKey = this.szKey;
            native.cbKey = checked((uint)this.cbKey);
            native.grbit = unchecked((uint)this.grbit);
            native.ulDensity = checked((uint)this.ulDensity);
            native.cbVarSegMac = new IntPtr(this.cbVarSegMac);
            native.cConditionalColumn = checked((uint)this.cConditionalColumn);
            return native;
        }
        /// 
        /// Gets the native (interop) version of this object, except for
        ///  and .
        /// 
        /// The native (interop) version of this object.
        internal NATIVE_INDEXCREATE1 GetNativeIndexcreate1()
        {
            var native = new NATIVE_INDEXCREATE1();
            native.indexcreate = this.GetNativeIndexcreate();
            native.indexcreate.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_INDEXCREATE1)));
            if (0 != this.cbKeyMost)
            {
                native.cbKeyMost = checked((uint)this.cbKeyMost);
                native.indexcreate.grbit |= unchecked((uint)VistaGrbits.IndexKeyMost);
            }
            return native;
        }
        /// 
        /// Gets the native (interop) version of this object. The following members
        /// are not converted:
        /// , , .
        /// 
        /// The native (interop) version of this object.
        internal NATIVE_INDEXCREATE2 GetNativeIndexcreate2()
        {
            var native = new NATIVE_INDEXCREATE2();
            native.indexcreate1 = this.GetNativeIndexcreate1();
            native.indexcreate1.indexcreate.cbStruct = checked((uint)Marshal.SizeOf(typeof(NATIVE_INDEXCREATE2)));
            // pSpaceHints conversion is done at pinvoke time.
            return native;
        }
        /// 
        /// Sets only the output fields of the object from a NATIVE_INDEXCREATE2 struct,
        /// specifically .
        /// 
        /// 
        /// The native indexcreate to set the values from.
        /// 
        internal void SetFromNativeIndexCreate(NATIVE_INDEXCREATE2 value)
        {
            this.SetFromNativeIndexCreate(value.indexcreate1);
        }
        /// 
        /// Sets only the output fields of the object from a NATIVE_INDEXCREATE1 struct,
        /// specifically .
        /// 
        /// 
        /// The native indexcreate to set the values from.
        /// 
        internal void SetFromNativeIndexCreate(NATIVE_INDEXCREATE1 value)
        {
            this.SetFromNativeIndexCreate(value.indexcreate);
        }
        /// 
        /// Sets only the output fields of the object from a native NATIVE_INDEXCREATE struct,
        /// specifically .
        /// 
        /// 
        /// The native indexcreate to set the values from.
        /// 
        internal void SetFromNativeIndexCreate(NATIVE_INDEXCREATE value)
        {
            this.err = (JET_err)value.err;
        }
#endif // !MANAGEDESENT_ON_WSA
        /// 
        /// Returns a value indicating whether the pidxUnicode member of this
        /// instance is equal to another instance.
        /// 
        /// An instance to compare with this instance.
        /// True if the pidxUnicode members of two instances are equal.
        private bool IsUnicodeIndexEqual(JET_INDEXCREATE other)
        {
            return (null == this.pidxUnicode)
                       ? (null == other.pidxUnicode)
                       : this.pidxUnicode.ContentEquals(other.pidxUnicode);
        }
        /// 
        /// Returns a value indicating whether the conditional column members of this
        /// instance is equal to another instance.
        /// 
        /// An instance to compare with this instance.
        /// True if the conditional column members of two instances are equal.
        private bool AreConditionalColumnsEqual(JET_INDEXCREATE other)
        {
            if (this.cConditionalColumn != other.cConditionalColumn)
            {
                return false;
            }
            for (int i = 0; i < this.cConditionalColumn; ++i)
            {
                if (!this.rgconditionalcolumn[i].ContentEquals(other.rgconditionalcolumn[i]))
                {
                    return false;
                }
            }
            return true;
        }
#if !MANAGEDESENT_ON_WSA // Not exposed in MSDK
        /// 
        /// The native version of the JET_INDEXCREATE structure.
        /// 
        [StructLayout(LayoutKind.Sequential)]
        [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules",
                         "SA1305:FieldNamesMustNotUseHungarianNotation",
                         Justification = "This should match the unmanaged API, which isn't capitalized.")]
        [SuppressMessage(
            "Microsoft.StyleCop.CSharp.NamingRules",
            "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter",
            Justification = "This should match the unmanaged API, which isn't capitalized.")]
        internal unsafe struct NATIVE_INDEXCREATE
        {
            /// 
            /// Size of the structure.
            /// 
            public uint cbStruct;
            /// 
            /// Name of the index.
            /// 
            public IntPtr szIndexName;
            /// 
            /// Index key description.
            /// 
            public IntPtr szKey;
            /// 
            /// Size of index key description.
            /// 
            public uint cbKey;
            /// 
            /// Index options.
            /// 
            public uint grbit;
            /// 
            /// Index density.
            /// 
            public uint ulDensity;
            /// 
            /// Pointer to unicode sort options.
            /// 
            public NATIVE_UNICODEINDEX* pidxUnicode;
            /// 
            /// Maximum size of column data to index. This can also be
            /// a pointer to a JET_TUPLELIMITS structure.
            /// 
            public IntPtr cbVarSegMac;
            /// 
            /// Pointer to array of conditional columns.
            /// 
            public IntPtr rgconditionalcolumn;
            /// 
            /// Count of conditional columns.
            /// 
            public uint cConditionalColumn;
            /// 
            /// Returned error from index creation.
            /// 
            public int err;
        }
        /// 
        /// The native version of the JET_INDEXCREATE2 structure. Introduced in Windows 7,
        /// this includes a  member.
        /// 
        [StructLayout(LayoutKind.Sequential)]
        [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules",
                         "SA1305:FieldNamesMustNotUseHungarianNotation",
                         Justification = "This should match the unmanaged API, which isn't capitalized.")]
        [SuppressMessage(
            "Microsoft.StyleCop.CSharp.NamingRules",
            "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter",
            Justification = "This should match the unmanaged API, which isn't capitalized.")]
        internal struct NATIVE_INDEXCREATE2
        {
            /// 
            /// Nested NATIVE_INDEXCREATE1 structure.
            /// 
            public NATIVE_INDEXCREATE1 indexcreate1;
            /// 
            /// A  pointer.
            /// 
            public IntPtr pSpaceHints;
        }
        /// 
        /// The native version of the JET_INDEXCREATE structure. This version includes the cbKeyMost
        /// member, which is only valid on Windows Vista and above, but the name of the structure
        /// was not changed for Vista.
        /// 
        [StructLayout(LayoutKind.Sequential)]
        [SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules",
                         "SA1305:FieldNamesMustNotUseHungarianNotation",
                         Justification = "This should match the unmanaged API, which isn't capitalized.")]
        [SuppressMessage(
            "Microsoft.StyleCop.CSharp.NamingRules",
            "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter",
            Justification = "This should match the unmanaged API, which isn't capitalized.")]
        internal struct NATIVE_INDEXCREATE1
        {
            /// 
            /// Nested NATIVE_INDEXCREATE structure.
            /// 
            public NATIVE_INDEXCREATE indexcreate;
            /// 
            /// Maximum size of the key.
            /// 
            public uint cbKeyMost;
        }
#endif // !MANAGEDESENT_ON_WSA
    }
}