//-----------------------------------------------------------------------
// 
//     Copyright (c) Microsoft Corporation.
// 
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    using Microsoft.Isam.Esent.Interop.Implementation;
    /// 
    /// Helper methods for the ESENT API. These methods deal with database
    /// meta-data.
    /// 
    public static partial class Api
    {
        #region Simpler API. Overloads that omit unused/obsolete parameters.
        /// 
        /// Initialize a new ESENT session.
        /// 
        /// The initialized instance to create the session in.
        /// Returns the created session.
        public static void BeginSession(JET_INSTANCE instance, out JET_SESID sesid)
        {
            Api.JetBeginSession(instance, out sesid, null, null);
        }
        /// 
        /// Creates and attaches a database file.
        /// 
        /// The session to use.
        /// The path to the database file to create.
        /// Returns the dbid of the new database.
        /// Database creation options.
        public static void CreateDatabase(JET_SESID sesid, string database, out JET_DBID dbid, CreateDatabaseGrbit grbit)
        {
            Api.JetCreateDatabase(sesid, database, null, out dbid, grbit);
        }
        /// 
        /// Opens a database previously attached with ,
        /// for use with a database session. This function can be called multiple times
        /// for the same database.
        /// 
        /// The session that is opening the database.
        /// The database to open.
        /// Returns the dbid of the attached database.
        /// Open database options.
        /// An ESENT warning code.
        public static JET_wrn OpenDatabase(
            JET_SESID sesid,
            string database,
            out JET_DBID dbid,
            OpenDatabaseGrbit grbit)
        {
            return Api.JetOpenDatabase(sesid, database, null, out dbid, grbit);
        }
        /// 
        /// Opens a cursor on a previously created table.
        /// 
        /// The database session to use.
        /// The database to open the table in.
        /// The name of the table to open.
        /// Table open options.
        /// Returns the opened table.
        /// An ESENT warning.
        public static JET_wrn OpenTable(
            JET_SESID sesid,
            JET_DBID dbid,
            string tablename,
            OpenTableGrbit grbit,
            out JET_TABLEID tableid)
        {
            return Api.JetOpenTable(sesid, dbid, tablename, null, 0, grbit, out tableid);
        }
        #endregion
        /// 
        /// Try to open a table.
        /// 
        /// The session to use.
        /// The database to look for the table in.
        /// The name of the table.
        /// Table open options.
        /// Returns the opened tableid.
        /// True if the table was opened, false if the table doesn't exist.
        public static bool TryOpenTable(
            JET_SESID sesid,
            JET_DBID dbid,
            string tablename,
            OpenTableGrbit grbit,
            out JET_TABLEID tableid)
        {
            var err = (JET_err)Impl.JetOpenTable(sesid, dbid, tablename, null, 0, grbit, out tableid);
            if (JET_err.ObjectNotFound == err)
            {
                return false;
            }
            Api.Check((int)err);
            Debug.Assert(err >= JET_err.Success, "Exception should have been thrown in case of error");
            return true;
        }
        /// 
        /// Creates a dictionary which maps column names to their column IDs.
        /// 
        /// The sesid to use.
        /// The table to retrieve the information for.
        /// A dictionary mapping column names to column IDs.
        public static IDictionary GetColumnDictionary(JET_SESID sesid, JET_TABLEID tableid)
        {
            JET_COLUMNLIST columnlist;
            JetGetTableColumnInfo(sesid, tableid, string.Empty, out columnlist);
            // As of Sep 2015, JetGetColumnInfoW is only called for Win8+. Even though Unicode should have
            // worked in Win7, it wasn't reliable until Win8.
            Encoding encodingOfTextColumns = EsentVersion.SupportsWindows8Features ? Encoding.Unicode : LibraryHelpers.EncodingASCII;
            try
            {
                // esent treats column names as case-insensitive, so we want the dictionary to be case insensitive as well
                var dict = new Dictionary(
                    columnlist.cRecord, StringComparer.OrdinalIgnoreCase);
                if (columnlist.cRecord > 0)
                {
                    if (Api.TryMoveFirst(sesid, columnlist.tableid))
                    {
                        do
                        {
                            string name = RetrieveColumnAsString(
                                sesid,
                                columnlist.tableid,
                                columnlist.columnidcolumnname,
                                encodingOfTextColumns,
                                RetrieveColumnGrbit.None);
                            name = StringCache.TryToIntern(name);
                            var columnidValue =
                                (uint)RetrieveColumnAsUInt32(sesid, columnlist.tableid, columnlist.columnidcolumnid);
                            var columnid = new JET_COLUMNID { Value = columnidValue };
                            dict.Add(name, columnid);
                        }
                        while (TryMoveNext(sesid, columnlist.tableid));
                    }
                }
                return dict;
            }
            finally
            {
                // Close the temporary table used to return the results
                JetCloseTable(sesid, columnlist.tableid);
            }
        }
        /// 
        /// Get the columnid of the specified column.
        /// 
        /// The session to use.
        /// The table containing the column.
        /// The name of the column.
        /// The id of the column.
        public static JET_COLUMNID GetTableColumnid(JET_SESID sesid, JET_TABLEID tableid, string columnName)
        {
            JET_COLUMNDEF columndef;
            JetGetTableColumnInfo(sesid, tableid, columnName, out columndef);
            return columndef.columnid;
        }
        /// 
        /// Iterates over all the columns in the table, returning information about each one.
        /// 
        /// The session to use.
        /// The table to retrieve column information for.
        /// An iterator over ColumnInfo for each column in the table.
        public static IEnumerable GetTableColumns(JET_SESID sesid, JET_TABLEID tableid)
        {
            return new GenericEnumerable(() => new TableidColumnInfoEnumerator(sesid, tableid));
        }
        /// 
        /// Iterates over all the columns in the table, returning information about each one.
        /// 
        /// The session to use.
        /// The database containing the table.
        /// The name of the table.
        /// An iterator over ColumnInfo for each column in the table.
        public static IEnumerable GetTableColumns(JET_SESID sesid, JET_DBID dbid, string tablename)
        {
            if (null == tablename)
            {
                throw new ArgumentNullException("tablename");
            }
            return new GenericEnumerable(() => new TableColumnInfoEnumerator(sesid, dbid, tablename));
        }
        /// 
        /// Iterates over all the indexes in the table, returning information about each one.
        /// 
        /// The session to use.
        /// The table to retrieve index information for.
        /// An iterator over an IndexInfo for each index in the table.
        public static IEnumerable GetTableIndexes(JET_SESID sesid, JET_TABLEID tableid)
        {
            return new GenericEnumerable(() => new TableidIndexInfoEnumerator(sesid, tableid));
        }
        /// 
        /// Iterates over all the indexs in the table, returning information about each one.
        /// 
        /// The session to use.
        /// The database containing the table.
        /// The name of the table.
        /// An iterator over an IndexInfo for each index in the table.
        public static IEnumerable GetTableIndexes(JET_SESID sesid, JET_DBID dbid, string tablename)
        {
            if (null == tablename)
            {
                throw new ArgumentNullException("tablename");
            }
            return new GenericEnumerable(() => new TableIndexInfoEnumerator(sesid, dbid, tablename));
        }
        /// 
        /// Returns the names of the tables in the database.
        /// 
        /// The session to use.
        /// The database containing the table.
        /// An iterator over the names of the tables in the database.
        public static IEnumerable GetTableNames(JET_SESID sesid, JET_DBID dbid)
        {
            return new GenericEnumerable(() => new TableNameEnumerator(sesid, dbid));
        }
        /// 
        /// Retrieves information about indexes on a table.
        /// 
        /// The session to use.
        /// The table to retrieve index information about.
        /// The name of the index.
        /// Filled in with information about indexes on the table.
        /// The type of information to retrieve.
        /// true if there was no error, false if the index wasn't found. Throws for other Jet errors.
        public static bool TryJetGetTableIndexInfo(
            JET_SESID sesid,
            JET_TABLEID tableid,
            string indexname,
            out JET_INDEXID result,
            JET_IdxInfo infoLevel)
        {
            int err = Impl.JetGetTableIndexInfo(sesid, tableid, indexname, out result, infoLevel);
            if ((JET_err)err == JET_err.IndexNotFound)
            {
                return false;
            }
            Api.Check(err);
            return true;
        }
        /// 
        /// Determines the name of the current index of a given cursor.
        /// 
        /// 
        /// This name is also used to later re-select that index as the current index using
        /// .  It can also be used to discover the properties of that index using
        /// JetGetTableIndexInfo.
        /// 
        /// The returned name of the index will be null if the current index is the clustered index and no primary
        /// index was explicitly defined.
        /// 
        /// The session to use.
        /// The cursor to get the index name for.
        /// Returns the name of the index.
        public static string JetGetCurrentIndex(JET_SESID sesid, JET_TABLEID tableid)
        {
            string indexName;
            Api.JetGetCurrentIndex(sesid, tableid, out indexName, SystemParameters.NameMost);
            return string.IsNullOrEmpty(indexName) ? null : indexName;
        }
    }
}