/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2009 Oracle.  All rights reserved.
 *
 */
using System;
using System.Collections.Generic;
using System.Text;

namespace BerkeleyDB {
    /// <summary>
    /// A class representing configuration parameters for
    /// <see cref="RecnoDatabase"/>
    /// </summary>
    public class SecondaryRecnoDatabaseConfig : SecondaryDatabaseConfig {
        /* Fields for DB->set_flags() */
        /// <summary>
        /// Cause the logical record numbers to be mutable, and change as
        /// records are added to and deleted from the database.
        /// </summary>
        /// <remarks>
        /// <para>
        /// For example, the deletion of record number 4 causes records numbered
        /// 5 and greater to be renumbered downward by one. If a cursor was
        /// positioned to record number 4 before the deletion, it will refer to
        /// the new record number 4, if any such record exists, after the
        /// deletion. If a cursor was positioned after record number 4 before
        /// the deletion, it will be shifted downward one logical record,
        /// continuing to refer to the same record as it did before.
        /// </para>
        /// <para>
        /// Using <see cref="Database.Put"/> or <see cref="Cursor.Put"/> to
        /// create new records will cause the creation of multiple records if
        /// the record number is more than one greater than the largest record
        /// currently in the database. For example, creating record 28, when
        /// record 25 was previously the last record in the database, will
        /// create records 26 and 27 as well as 28. Attempts to retrieve records
        /// that were created in this manner will throw a
        /// <see cref="KeyEmptyException"/>.
        /// </para>
        /// <para>
        /// If a created record is not at the end of the database, all records
        /// following the new record will be automatically renumbered upward by
        /// one. For example, the creation of a new record numbered 8 causes
        /// records numbered 8 and greater to be renumbered upward by one. If a
        /// cursor was positioned to record number 8 or greater before the
        /// insertion, it will be shifted upward one logical record, continuing
        /// to refer to the same record as it did before.
        /// </para>
        /// <para>
        /// For these reasons, concurrent access to a
        /// <see cref="SecondaryRecnoDatabase"/> with this setting specified may
        /// be largely meaningless, although it is supported.
        /// </para>
        /// <para>
        /// If the database already exists, this setting must be the same as the
        /// existing database or an exception will be thrown.
        /// </para>
        /// </remarks>
        public bool Renumber;
        /// <summary>
        /// If true, any <see cref="BackingFile"/> file will be read in its
        /// entirety when <see cref="SecondaryRecnoDatabase.Open"/> is called.
        /// If false, <see cref="BackingFile"/> may be read lazily. 
        /// </summary>
        public bool Snapshot;
        internal new uint flags {
            get {
                uint ret = base.flags;
                ret |= Renumber ? Internal.DbConstants.DB_RENUMBER : 0;
                ret |= Snapshot ? Internal.DbConstants.DB_SNAPSHOT : 0;
                return ret;
            }
        }

        /// <summary>
        /// The policy for how to handle database creation.
        /// </summary>
        /// <remarks>
        /// If the database does not already exist and
        /// <see cref="CreatePolicy.NEVER"/> is set,
        /// <see cref="SecondaryRecnoDatabase.Open"/> will fail.
        /// </remarks>
        public CreatePolicy Creation;
        internal new uint openFlags {
            get {
                uint flags = base.openFlags;
                flags |= (uint)Creation;
                return flags;
            }
        }


        internal bool delimiterIsSet;
        private int delim;
        /// <summary>
        /// The delimiting byte used to mark the end of a record in
        /// <see cref="BackingFile"/>.
        /// </summary>
        /// <remarks>
        /// <para>
        /// This byte is used for variable length records if
        /// <see cref="BackingFile"/> is set. If <see cref="BackingFile"/> is
        /// specified and no delimiting byte was specified, newline characters
        /// (that is, ASCII 0x0a) are interpreted as end-of-record markers.
        /// </para>
        /// <para>
        /// If the database already exists, this setting will be ignored.
        /// </para>
        /// </remarks>
        public int Delimiter {
            get { return delim; }
            set {
                delimiterIsSet = true;
                delim = value;
            }
        }

        internal bool lengthIsSet;
        private uint len;
        /// <summary>
        /// Specify that the records are fixed-length, not byte-delimited, and
        /// are of length Length. 
        /// </summary>
        /// <remarks>
        /// <para>
        /// Any records added to the database that are less than Length bytes
        /// long are automatically padded (see <see cref="PadByte"/> for more
        /// information).
        /// </para>
        /// <para>
        /// Any attempt to insert records into the database that are greater
        /// than Length bytes long will cause the call to fail immediately and
        /// return an error. 
        /// </para>
        /// <para>
        /// If the database already exists, this setting will be ignored.
        /// </para>
        /// </remarks>
        public uint Length {
            get { return len; }
            set {
                lengthIsSet = true;
                len = value;
            }
        }

        internal bool padIsSet;
        private int pad;
        /// <summary>
        /// The padding character for short, fixed-length records.
        /// </summary>
        /// <remarks>
        /// <para>
        /// If no pad character is specified, space characters (that is, ASCII
        /// 0x20) are used for padding.
        /// </para>
        /// <para>
        /// If the database already exists, this setting will be ignored.
        /// </para>
        /// </remarks>
        public int PadByte {
            get { return pad; }
            set {
                padIsSet = true;
                pad = value;
            }
        }

        /// <summary>
        /// The underlying source file for the Recno access method.
        /// </summary>
        /// <remarks>
        /// <para>
        /// The purpose of the source file is to provide fast access and
        /// modification to databases that are normally stored as flat text
        /// files.
        /// </para>
        /// <para>
        /// The source parameter specifies an underlying flat text database file
        /// that is read to initialize a transient record number index. In the
        /// case of variable length records, the records are separated, as
        /// specified by <see cref="Delimiter"/>. For example, standard UNIX
        /// byte stream files can be interpreted as a sequence of variable
        /// length records separated by newline characters.
        /// </para>
        /// <para>
        /// In addition, when cached data would normally be written back to the
        /// underlying database file (for example,
        /// <see cref="BaseDatabase.Close"/> or
        /// <see cref="BaseDatabase.Sync"/>), the in-memory copy of the
        /// database will be written back to the source file.
        /// </para>
        /// <para>
        /// By default, the backing source file is read lazily; that is, records
        /// are not read from the file until they are requested by the
        /// application. If multiple processes (not threads) are accessing a
        /// Recno database concurrently, and are either inserting or deleting
        /// records, the backing source file must be read in its entirety before
        /// more than a single process accesses the database, and only that
        /// process should specify the backing source file as part of the
        /// <see cref="SecondaryRecnoDatabase.Open"/> call. See
        /// <see cref="Snapshot"/> for more information.
        /// </para>
        /// <para>
        /// Reading and writing the backing source file specified by source
        /// cannot be transaction-protected because it involves filesystem
        /// operations that are not part of the Db transaction methodology. For
        /// this reason, if a temporary database is used to hold the records, it
        /// is possible to lose the contents of the source file, for example, if
        /// the system crashes at the right instant. If a file is used to hold
        /// the database, normal database recovery on that file can be used to
        /// prevent information loss, although it is still possible that the
        /// contents of source will be lost if the system crashes.
        /// </para>
        /// <para>
        /// The source file must already exist (but may be zero-length) when 
        /// <see cref="SecondaryRecnoDatabase.Open"/> is called.
        /// </para>
        /// <para>
        /// It is not an error to specify a read-only source file when creating
        /// a database, nor is it an error to modify the resulting database.
        /// However, any attempt to write the changes to the backing source file
        /// using either the <see cref="BaseDatabase.Sync"/> or
        /// <see cref="BaseDatabase.Close"/> methods will fail, of course.
        /// Use <see cref="BaseDatabase.Close(bool)"/> to stop it from
        /// attempting to write the changes to the backing file; instead, they
        /// will be silently discarded.
        /// </para>
        /// <para>
        /// For all of the previous reasons, the source file is generally used
        /// to specify databases that are read-only for Berkeley DB
        /// applications; and that are either generated on the fly by software
        /// tools or modified using a different mechanism — for example, a text
        /// editor.
        /// </para>
        /// <para>
        /// If the database already exists, BackingFile must be the same as that
        /// historically used to create the database or corruption can occur.
        /// </para>
        /// </remarks>
        public string BackingFile;

        /// <summary>
        /// Instantiate a new SecondaryRecnoDatabaseConfig object
        /// </summary>
        public SecondaryRecnoDatabaseConfig(
            Database PrimaryDB, SecondaryKeyGenDelegate KeyGenFunc)
            : base(PrimaryDB, KeyGenFunc) {
            Renumber = false;
            Snapshot = false;
            delimiterIsSet = false;
            lengthIsSet = false;
            padIsSet = false;
            BackingFile = null;
            DbType = DatabaseType.RECNO;
        }
    }
}