/*
 *******************************************************************************
 *
 *  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.cpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Database error signaling/handling (C++ wrapper code)
 * \version 2015-10-14/AW: Initial version
 * \version 2020-02-21/AW: Class 'Error' is now context-free (#3544)
 *
 *******************************************************************************
 */

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

#include <cstdlib>

#include "riegl/rdb.h"
#include "riegl/rdb.hpp"

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

namespace riegl {
namespace rdb {

//---< CLASS RDBStringWrapper >-------------------------------------------------

class RDBStringWrapper
{
public:
    RDBString str;

    RDBStringWrapper();
    ~RDBStringWrapper();

    operator std::string() const;
};

//---< CLASS Error >------------------------------------------------------------

Error::Error(
    const int         &code,
    const std::string &details
):
    errorCode   (code),
    errorDetails(details)
{
}

Error::~Error()
{
}

const int& Error::code() const RDB_NO_EXCEPT
{
    return errorCode;
}

const char* Error::what() const RDB_NO_EXCEPT
{
    return nullptr; // see class ErrorImpl
}

const char* Error::details() const RDB_NO_EXCEPT
{
    return nullptr; // see class ErrorImpl
}

//---< CLASS ErrorImpl >--------------------------------------------------------

class ErrorImpl: public Error
{
public:
    explicit ErrorImpl(
        const int         &code,
        const std::string &text,
        const std::string &details
    ):
        Error       (code),
        errorText   (text),
        errorDetails(details)
    {
    }

    virtual ~ErrorImpl()
    {
    }

    virtual const char* what() const RDB_NO_EXCEPT
    {
        return errorText.c_str();
    }

    virtual const char* details() const RDB_NO_EXCEPT
    {
        return errorDetails.c_str();
    }

    static bool check(
              RDBContext *context,       //!< library context
        const RDBResult  &code,          //!< C-API call return value
        const bool       &silent = false //!< false: raise exception (riegl::rdb::Error), true: set return-value only
    )
    {
        const bool success(code == RDB_SUCCESS);
        if ((!success) && (!silent))
        {
            std::int32_t errorCode(0);       //  is an
            RDBString    errorText(nullptr); // unknown
            RDBString    errorMore(nullptr); //  error
            if (context)
            {
                rdb_context_get_last_error(context, &errorCode, &errorText, &errorMore);
            }
            throw ErrorImpl(
                errorCode,
                std::string(errorText ? errorText : "unknown"),
                std::string(errorMore ? errorMore : "")
            );
        }
        return success;
    }

    static bool check(
        RDBResultCF result,        //!< C-API context-free function call return value
        const bool &silent = false //!< false: raise exception (riegl::rdb::Error), true: set return-value only
    )
    {
        const bool success(result == nullptr);
        if (!success)
        {
            std::int32_t     errorCode(0); //  is an
            RDBStringWrapper errorText;    // unknown
            RDBStringWrapper errorMore;    //  error

            check(rdb_result_get_error_code_cf   (result, &errorCode),     true);
            check(rdb_result_get_error_text_cf   (result, &errorText.str), true);
            check(rdb_result_get_error_details_cf(result, &errorMore.str), true);
            check(rdb_result_delete_cf(&result), true);

            if (!silent) throw ErrorImpl(
                errorCode,
                std::string(errorText.str ? errorText.str : "unknown"),
                std::string(errorMore.str ? errorMore.str : "")
            );
        }
        return success;
    }

private:
    const std::string errorText;
    const std::string errorDetails;
};

//---< CLASS RDBStringWrapper::PUBLIC >-----------------------------------------

RDBStringWrapper::RDBStringWrapper(): str(nullptr)
{
}

RDBStringWrapper::~RDBStringWrapper()
{
    if (str) // then delete the string and ignore errors
    {
        ErrorImpl::check(rdb_string_delete_cf(&str), true);
    }
}

RDBStringWrapper::operator std::string() const
{
    return std::string(str ? str : "");
}

}} // namespace riegl::rdb
