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

namespace BerkeleyDB {
    /// <summary>
    /// Represents errors that occur during Berkley DB operations.
    /// </summary>
    public class DatabaseException : Exception {
        /// <summary>
        /// The underlying error code from the Berkeley DB C library.
        /// </summary>
        public int ErrorCode;

        /// <summary>
        /// Throw an exception which corresponds to the specified Berkeley DB
        /// error code.
        /// </summary>
        /// <param name="err">The Berkeley DB error code</param>
        public static void ThrowException(int err) {
            switch (err) {
                case 0:
                    return;
                case ErrorCodes.DB_NOTFOUND:
                    throw new NotFoundException();
                case ErrorCodes.DB_BUFFER_SMALL:
                    throw new MemoryException();
                case ErrorCodes.DB_FOREIGN_CONFLICT:
                    throw new ForeignConflictException();
                case ErrorCodes.DB_KEYEMPTY:
                    throw new KeyEmptyException();
                case ErrorCodes.DB_KEYEXIST:
                    throw new KeyExistException();
                case ErrorCodes.DB_LOCK_DEADLOCK:
                    throw new DeadlockException();
                case ErrorCodes.DB_LOCK_NOTGRANTED:
                    throw new LockNotGrantedException();
                case ErrorCodes.DB_LOG_BUFFER_FULL:
                    throw new FullLogBufferException();
                case ErrorCodes.DB_OLD_VERSION:
                    throw new OldVersionException();
                case ErrorCodes.DB_PAGE_NOTFOUND:
                    throw new PageNotFoundException();
                case ErrorCodes.DB_REP_DUPMASTER:
                case ErrorCodes.DB_REP_HOLDELECTION:
                case ErrorCodes.DB_REP_IGNORE:
                case ErrorCodes.DB_REP_ISPERM:
                case ErrorCodes.DB_REP_JOIN_FAILURE:
                case ErrorCodes.DB_REP_NEWSITE:
                case ErrorCodes.DB_REP_NOTPERM:
                    return;
                case ErrorCodes.DB_REP_LEASE_EXPIRED:
                    throw new LeaseExpiredException();
                case ErrorCodes.DB_RUNRECOVERY:
                    throw new RunRecoveryException();
                case ErrorCodes.DB_SECONDARY_BAD:
                    throw new BadSecondaryException();
                case ErrorCodes.DB_VERIFY_BAD:
                    throw new VerificationException();
                case ErrorCodes.DB_VERSION_MISMATCH:
                    throw new VersionMismatchException();
                default:
                    throw new DatabaseException(err);
            }
        }

        /// <summary>
        /// Create a new DatabaseException, encapsulating a specific error code.
        /// </summary>
        /// <param name="err">The error code to encapsulate.</param>
        public DatabaseException(int err)
            : base(libdb_csharp.db_strerror(err)) {
            ErrorCode = err;
        }
    }

    /// <summary>
    /// A secondary index has been corrupted. This is likely the result of an
    /// application operating on related databases without first associating
    /// them.
    /// </summary>
    public class BadSecondaryException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the BadSecondaryException
        /// </summary>
        public BadSecondaryException() : base(ErrorCodes.DB_SECONDARY_BAD) { }
    }

    /// <summary>
    /// 
    /// </summary>
    public class ForeignConflictException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the ForeignConflictException
        /// </summary>
        public ForeignConflictException()
            : base(ErrorCodes.DB_FOREIGN_CONFLICT) { }
    }

    /// <summary>
    /// In-memory logs are configured and no more log buffer space is available.
    /// </summary>
    public class FullLogBufferException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the FullLogBufferException
        /// </summary>
        public FullLogBufferException()
            : base(ErrorCodes.DB_LOG_BUFFER_FULL) { }
    }

    /// <summary>
    /// The requested key/data pair logically exists but was never explicitly
    /// created by the application, or that the requested key/data pair was
    /// deleted and never re-created. In addition, the Queue access method will
    /// throw a KeyEmptyException for records that were created as part of a
    /// transaction that was later aborted and never re-created.
    /// </summary>
    /// <remarks>
    /// The Recno and Queue access methods will automatically create key/data
    /// pairs under some circumstances.
    /// </remarks>
    public class KeyEmptyException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the KeyEmptyException
        /// </summary>
        public KeyEmptyException() : base(ErrorCodes.DB_KEYEMPTY) { }
    }

    /// <summary>
    /// A key/data pair was inserted into the database using
    /// <see cref="Database.PutNoOverwrite"/> and the key already
    /// exists in the database, or using
    /// <see cref="BTreeDatabase.PutNoDuplicate"/> or
    /// <see cref="HashDatabase.PutNoDuplicate"/> and the key/data
    /// pair already exists in the database.
    /// </summary>
    public class KeyExistException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the KeyExistException
        /// </summary>
        public KeyExistException() : base(ErrorCodes.DB_KEYEXIST) { }
    }

    /// <summary>
    /// When multiple threads of control are modifying the database, there is
    /// normally the potential for deadlock. In Berkeley DB, deadlock is
    /// signified by a DeadlockException thrown from the Berkeley DB function.
    /// Whenever a Berkeley DB function throws a DeadlockException, the
    /// enclosing transaction should be aborted.
    /// </summary>
    public class DeadlockException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the DeadlockException
        /// </summary>
        public DeadlockException() : base(ErrorCodes.DB_LOCK_DEADLOCK) { }
    }

    /// <summary>
    /// The site's replication master lease has expired.
    /// </summary>
    public class LeaseExpiredException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the LeaseExpiredException
        /// </summary>
        public LeaseExpiredException()
            : base(ErrorCodes.DB_REP_LEASE_EXPIRED) { }
    }

    /// <summary>
    /// If <see cref="DatabaseEnvironmentConfig.TimeNotGranted"/> is true,
    /// database calls timing out based on lock or transaction timeout values
    /// will throw a LockNotGrantedException, instead of a DeadlockException.
    /// </summary>
    public class LockNotGrantedException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the LockNotGrantedException
        /// </summary>
        public LockNotGrantedException()
            : base(ErrorCodes.DB_LOCK_NOTGRANTED) { }
    }

    internal class MemoryException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the MemoryException
        /// </summary>
        internal MemoryException() : base(ErrorCodes.DB_BUFFER_SMALL) { }
    }

    /// <summary>
    /// The requested key/data pair did not exist in the database or that
    /// start-of- or end-of-file has been reached by a cursor.
    /// </summary>
    public class NotFoundException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the NotFoundException
        /// </summary>
        public NotFoundException() : base(ErrorCodes.DB_NOTFOUND) { }
    }

    /// <summary>
    /// This version of Berkeley DB is unable to upgrade a given database.
    /// </summary>
    public class OldVersionException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the OldVersionException
        /// </summary>
        public OldVersionException() : base(ErrorCodes.DB_OLD_VERSION) { }
    }

    /// <summary>
    /// 
    /// </summary>
    public class PageNotFoundException : DatabaseException {
        /// <summary>
        /// 
        /// </summary>
        public PageNotFoundException() : base(ErrorCodes.DB_PAGE_NOTFOUND) { }
    }

    /// <summary>
    /// Berkeley DB has encountered an error it considers fatal to an entire
    /// environment. Once a RunRecoveryException has been thrown by any
    /// interface, it will be returned from all subsequent Berkeley DB calls
    /// made by any threads of control participating in the environment.
    /// </summary>
    /// <remarks>
    /// An example of this type of fatal error is a corrupted database page. The
    /// only way to recover from this type of error is to have all threads of
    /// control exit the Berkeley DB environment, run recovery of the
    /// environment, and re-enter Berkeley DB. (It is not strictly necessary
    /// that the processes exit, although that is the only way to recover system
    /// resources, such as file descriptors and memory, allocated by
    /// Berkeley DB.)
    /// </remarks>
    public class RunRecoveryException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the RunRecoveryException
        /// </summary>
        public RunRecoveryException() : base(ErrorCodes.DB_RUNRECOVERY) { }
    }

    /// <summary>
    /// Thrown by <see cref="Database.Verify"/> if a database is
    /// corrupted, and by <see cref="Database.Salvage"/> if all
    /// key/data pairs in the file may not have been successfully output.
    /// </summary>
    public class VerificationException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the VerificationException
        /// </summary>
        public VerificationException() : base(ErrorCodes.DB_VERIFY_BAD) { }
    }

    /// <summary>
    /// The version of the Berkeley DB library doesn't match the version that
    /// created the database environment.
    /// </summary>
    public class VersionMismatchException : DatabaseException {
        /// <summary>
        /// Initialize a new instance of the VersionMismatchException
        /// </summary>
        public VersionMismatchException()
            : base(ErrorCodes.DB_VERSION_MISMATCH) { }
    }
}