/*
 *******************************************************************************
 *
 *  Copyright 2025 RIEGL Laser Measurement Systems
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  SPDX-License-Identifier: Apache-2.0
 *
 *******************************************************************************
 */
/*!
 *******************************************************************************
 *
 * \file    error.hpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Database error signaling/handling
 * \version 2015-10-14/AW: Initial version
 * \version 2017-11-09/AW: Error 'PointAttributeNotMergeable' added
 * \version 2017-11-24/AW: Constructors declared as "explicit" (#2825)
 * \version 2018-03-12/AW: Error 'PointAttributeInvalidInvalid' added (#3047)
 * \version 2018-11-20/AW: Error 'QueryInsertTooManyPoints' added (#3215)
 * \version 2019-05-13/AW: Error 'MetadataInvalidName' added
 * \version 2020-02-14/AW: Error 'MetadataTableTooLarge' added (#3560)
 * \version 2020-02-21/AW: Class 'Error' is now context-free (#3544)
 * \version 2020-03-30/AW: Error 'MetadataSignatureError' added (#3570)
 * \version 2021-06-02/AW: Error 'TransactionDetailsTooLarge' added (#3905)
 * \version 2021-06-02/AW: Error 'InvalidStringSize' added (#3905)
 * \version 2024-11-29/AW: Error 'QueryInsertFailed' added (#5339)
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_ERROR_HPP
#define RIEGL_RDB_ERROR_HPP

//---< INCLUDES >---------------------------------------------------------------

#include <string>
#include <exception>
#include "riegl/rdb.h"

//---< NAMESPACE >--------------------------------------------------------------

namespace riegl {
namespace rdb {

//---< CLASS Error >------------------------------------------------------------
/*!
 * \brief Database error class
 */
class Error: public std::exception
{
public:
    /*!
     * \brief List of error codes
     */
    enum ErrorCode
    {
        DatabaseOpenFailed              = 10100, //!< failed to create or open database, most likely insufficient access rights or invalid location
        DatabaseNotOpened               = 10101, //!< a query failed because no database was opened
        DatabaseNotWritable             = 10102, //!< modification of database not possible as opened in read-only mode (check file access rights)
        DatabaseVersionNotSupported     = 10103, //!< database file format version not supported
        DatabaseSchemaValidationFailed  = 10104, //!< database does not correspond to user-supplied schema
        //
        TransactionMissing              = 10201, //!< operation failed because there was no transaction, see pointcloud::Transactions::begin()
        TransactionPending              = 10202, //!< operation failed because a transaction is started but not yet finished
        TransactionCorrupted            = 10203, //!< operation failed because database was modified by concurrent client (should never happen)
        TransactionInvalid              = 10204, //!< given transaction ID is invalid
        TransactionLockAcquireFailed    = 10205, //!< failed to acquire database transaction lock
        TransactionLockReleaseFailed    = 10206, //!< failed to release database transaction lock
        TransactionDetailsTooLarge      = 10207, //!< total size of all transaction details strings exceeds the limit
        //
        PointAttributeDuplicate         = 10301, //!< point attribute of given name already exists
        PointAttributeMissing           = 10302, //!< point attribute of given name does not exist, attribute name is stored in error details
        PointAttributeInvalidLength     = 10303, //!< point attribute vector length is invalid
        PointAttributeInvalidLimits     = 10304, //!< point attribute minimum and maximum are invalid
        PointAttributeInvalidDefault    = 10305, //!< point attribute default value out of range (min./max.)
        PointAttributeInvalidResolution = 10306, //!< point attribute resolution is invalid
        PointAttributeCannotDuplicate   = 10307, //!< source point attribute data could not be copied to target point attribute data as they are not compatible
        PointAttributeCannotModify      = 10308, //!< modification of point attribute property not allowed (property name is stored in error details)
        PointAttributeInvalidScale      = 10309, //!< invalid point attribute scale factor given (value is stored in error details)
        PointAttributeNotMergeable      = 10310, //!< the point attribute details can not be merged because they are not compatible (reason see details) \see riegl::rdb::pointcloud::PointAttributes::getMerged()
        PointAttributeInvalidInvalid    = 10311, //!< point attribute invalid value out of range (min./max.)
        //
        MetadataValidationFailed        = 10312, //!< metadata entry does not correspond to schema
        MetadataInvalidName             = 10313, //!< metadata entry name is not allowed (name is stored in error details)
        MetadataTableTooLarge           = 10314, //!< total size of all metadata item names and values exceeds the limit
        MetadataSignatureError          = 10315, //!< failed to create or verify metadata entry signature (reason in error details)
        //
        QueryBindAttributeInvalid       = 10401, //!< bound invalid attribute to query, attribute name is stored in error details (not all attributes are allowed in all queries)
        QueryBindAttributeUnknown       = 10402, //!< bound unknown attribute to query, attribute name is stored in error details
        QueryBindAttributeIndexVoid     = 10403, //!< bound attribute with invalid vector index
        QueryBindAttributeBufferVoid    = 10404, //!< bound attribute buffer is invalid
        QuerySelectFilterParseFailed    = 10406, //!< select query filter string parsing failed, check syntax
        QuerySelectAttributeUnknown     = 10407, //!< select query filter string refers to unknown point attribute, attribute name is stored in error details
        QuerySelectVectorIndexMissing   = 10408, //!< select query filter string does not define vector index for vector point attribute
        QuerySelectVectorIndexInvalid   = 10409, //!< select query filter string contains invalid vector attribute index
        QuerySelectGraphNodeInvalid     = 10410, //!< select query node ID invalid
        QueryInsertNoPrimaryAttribute   = 10411, //!< no buffer for primary point attribute defined
        QueryUpdateConstantAttribute    = 10412, //!< tried to modify a constant attribute
        QueryStatIndexNodeNotFound      = 10413, //!< index graph node not found (invalid node ID)
        QueryAttributeValueOutOfRange   = 10414, //!< given attribute value (insert or update) is out of range, attribute name is stored in error details
        QueryPointIdentifierMissing     = 10415, //!< no buffer for point ID attribute defined
        QueryPending                    = 10416, //!< can't start a new query as an other query started by this Pointcloud instance is not finished yet
        QueryBindAttributeIncomplete    = 10417, //!< no buffer given for at least one element of an vector point attribute (i.e. when length > 1)
        QueryInsertTooManyPoints        = 10418, //!< points can not be inserted as the total number of points would exceed the limit of 65536 * points-per-chunk (defined at database creation); this limit only applies if the primary point attribute is "riegl.id" (point ID)
        QueryInsertFailed               = 10419, //!< point insertion query failed, check the error details for the reason
        //
        FeatureNotLicensed              = 10001, //!< no valid license key found for requested feature
        FailedToParseJSON               = 10002, //!< failed to parse JSON string, reason available in error details
        InvalidStringSize               = 10003, //!< invalid string size, string name and limit in error details
        Internal                        = 90000, //!< base error code for fatal internal errors
        Unknown                         =     1  //!< unknown error occurred
    };

public:
    explicit Error(
        const int         &code,        //!< error code \see enum ErrorCode
        const std::string &details = "" //!< optional error details (e.g. attribute name)
    ); //!< Constructor

    virtual ~Error(); //!< Destructor

    /*!
     * \brief Get error code
     *
     * \see enum ErrorCode
     */
    virtual const int& code() const RDB_NO_EXCEPT;

    /*!
     * \brief Get error text
     *
     * This function returns a basic English description text for the
     * error. The client application might use this in e.g. error logs.
     * Text that is displayed to the user should rather be derived from
     * the error code instead (e.g. localized error messages).
     */
    virtual const char* what() const RDB_NO_EXCEPT;

    /*!
     * \brief Get error details
     *
     * Depending on the error code, there might be additional error details.
     * For example, error QueryAttributeValueOutOfRange stores the attribute
     * name in the error details.
     *
     * To see which errors provide details, \see enum ErrorCode.
     */
    virtual const char* details() const RDB_NO_EXCEPT;

private:
    const int         errorCode;    //!< see constructor
    const std::string errorDetails; //!< see constructor
};

}} // namespace riegl::rdb

#endif // RIEGL_RDB_ERROR_HPP
