//-----------------------------------------------------------------------
// 
//     Copyright (c) Microsoft Corporation.
// 
//-----------------------------------------------------------------------
namespace Microsoft.Isam.Esent.Interop
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    /// 
    /// Helper methods for the ESENT API. These aren't interop versions
    /// of the API, but encapsulate very common uses of the functions.
    /// 
    public static partial class Api
    {
        /// 
        /// Position the cursor before the first record in the table. A
        /// subsequent move next will position the cursor on the first
        /// record.
        /// 
        /// The session to use.
        /// The table to position.
        public static void MoveBeforeFirst(JET_SESID sesid, JET_TABLEID tableid)
        {
            Api.TryMoveFirst(sesid, tableid);
            Api.TryMovePrevious(sesid, tableid);
        }
        /// 
        /// Position the cursor after the last record in the table. A
        /// subsequent move previous will position the cursor on the
        /// last record.
        /// 
        /// The session to use.
        /// The table to position.
        public static void MoveAfterLast(JET_SESID sesid, JET_TABLEID tableid)
        {
            Api.TryMoveLast(sesid, tableid);
            Api.TryMoveNext(sesid, tableid);
        }
        /// 
        /// Try to navigate through an index. If the navigation succeeds this
        /// method returns true. If there is no record to navigate to this
        /// method returns false; an exception will be thrown for other errors.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// The direction to move in.
        /// Move options.
        /// True if the move was successful.
        public static bool TryMove(JET_SESID sesid, JET_TABLEID tableid, JET_Move move, MoveGrbit grbit)
        {
            var err = (JET_err)Impl.JetMove(sesid, tableid, (int)move, grbit);
            if (JET_err.NoCurrentRecord == err)
            {
                return false;
            }
            Api.Check((int)err);
            Debug.Assert(err >= JET_err.Success, "Exception should have been thrown in case of error");
            return true;
        }
        /// 
        /// Try to move to the first record in the table. If the table is empty this
        /// returns false, if a different error is encountered an exception is thrown.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// True if the move was successful.
        public static bool TryMoveFirst(JET_SESID sesid, JET_TABLEID tableid)
        {
            return TryMove(sesid, tableid, JET_Move.First, MoveGrbit.None);
        }
        /// 
        /// Try to move to the last record in the table. If the table is empty this
        /// returns false, if a different error is encountered an exception is thrown.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// True if the move was successful.
        public static bool TryMoveLast(JET_SESID sesid, JET_TABLEID tableid)
        {
            return TryMove(sesid, tableid, JET_Move.Last, MoveGrbit.None);
        }
        /// 
        /// Try to move to the next record in the table. If there is not a next record
        /// this returns false, if a different error is encountered an exception is thrown.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// True if the move was successful.
        public static bool TryMoveNext(JET_SESID sesid, JET_TABLEID tableid)
        {
            return TryMove(sesid, tableid, JET_Move.Next, MoveGrbit.None);
        }
        /// 
        /// Try to move to the previous record in the table. If there is not a previous record
        /// this returns false, if a different error is encountered an exception is thrown.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// True if the move was successful.
        public static bool TryMovePrevious(JET_SESID sesid, JET_TABLEID tableid)
        {
            return TryMove(sesid, tableid, JET_Move.Previous, MoveGrbit.None);
        }
        /// 
        /// Efficiently positions a cursor to an index entry that matches the search
        /// criteria specified by the search key in that cursor and the specified
        /// inequality. A search key must have been previously constructed using JetMakeKey.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// Seek option.
        /// True if a record matching the criteria was found.
        public static bool TrySeek(JET_SESID sesid, JET_TABLEID tableid, SeekGrbit grbit)
        {
            var err = (JET_err)Impl.JetSeek(sesid, tableid, grbit);
            if (JET_err.RecordNotFound == err)
            {
                return false;
            }
            Api.Check((int)err);
            Debug.Assert(err >= JET_err.Success, "Exception should have been thrown in case of error");
            return true;
        }
        /// 
        /// Temporarily limits the set of index entries that the cursor can walk using
        /// JetMove to those starting from the current index entry and ending at the index
        /// entry that matches the search criteria specified by the search key in that cursor
        /// and the specified bound criteria. A search key must have been previously constructed
        /// using JetMakeKey. Returns true if the index range is non-empty, false otherwise.
        /// 
        /// The session to use.
        /// The cursor to position.
        /// Seek option.
        /// True if the seek was successful.
        public static bool TrySetIndexRange(JET_SESID sesid, JET_TABLEID tableid, SetIndexRangeGrbit grbit)
        {
            var err = (JET_err)Impl.JetSetIndexRange(sesid, tableid, grbit);
            if (JET_err.NoCurrentRecord == err)
            {
                return false;
            }
            Api.Check((int)err);
            Debug.Assert(err >= JET_err.Success, "Exception should have been thrown in case of error");
            return true;
        }
        /// 
        /// Removes an index range created with  or
        /// . If no index range is present this
        /// method does nothing.
        /// 
        /// The session to use.
        /// The cursor to remove the index range on.
        public static void ResetIndexRange(JET_SESID sesid, JET_TABLEID tableid)
        {
            var err = (JET_err)Impl.JetSetIndexRange(sesid, tableid, SetIndexRangeGrbit.RangeRemove);
            if (JET_err.InvalidOperation == err)
            {
                // this error is expected if there isn't currently an index range
                return;
            }
            Api.Check((int)err);
            return;
        }
        /// 
        /// Intersect a group of index ranges and return the bookmarks of the records which are found
        /// in all the index ranges.
        /// Also see .
        /// 
        /// The session to use.
        /// 
        /// The tableids to use. Each tableid must be from a different index on the same table and
        /// have an active index range. Use 
        /// to create an index range.
        /// 
        /// 
        /// The bookmarks of the records which are found in all the index ranges. The bookmarks
        /// are returned in primary key order.
        /// 
        public static IEnumerable IntersectIndexes(JET_SESID sesid, params JET_TABLEID[] tableids)
        {
            if (null == tableids)
            {
                throw new ArgumentNullException("tableids");
            }
            var ranges = new JET_INDEXRANGE[tableids.Length];
            for (int i = 0; i < tableids.Length; ++i)
            {
                ranges[i] = new JET_INDEXRANGE { tableid = tableids[i] };
            }
            return new GenericEnumerable(() => new IntersectIndexesEnumerator(sesid, ranges));
        }
        /// 
        /// Positions a cursor to an index entry for the record that is associated with
        /// the specified bookmark. The bookmark can be used with any index defined over
        /// a table. The bookmark for a record can be retrieved using .
        /// 
        /// The session to use.
        /// The cursor to position.
        /// The bookmark used to position the cursor.
        /// The size of the bookmark.
        /// True if a record matching the bookmark was found.
        public static bool TryGotoBookmark(JET_SESID sesid, JET_TABLEID tableid, byte[] bookmark, int bookmarkSize)
        {
            var err = (JET_err)Impl.JetGotoBookmark(sesid, tableid, bookmark, bookmarkSize);
            // Return false if the record no longer exists.
            if (JET_err.RecordDeleted == err)
            {
                return false;
            }
            // Return false if there is no entry for this record on the current (secondary) index.
            if (JET_err.NoCurrentRecord == err)
            {
                return false;
            }
            Api.Check((int)err);
            Debug.Assert(err >= JET_err.Success, "Exception should have been thrown in case of error");
            return true;
        }
    }
}