/***************************************************************************
 *   Copyright (c) 2002 Jürgen Riegel <juergen.riegel@web.de>              *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License (LGPL)   *
 *   as published by the Free Software Foundation; either version 2 of     *
 *   the License, or (at your option) any later version.                   *
 *   for detail see the LICENCE text file.                                 *
 *                                                                         *
 *   FreeCAD is distributed in the hope that it will be useful,            *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with FreeCAD; if not, write to the Free Software        *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
 *   USA                                                                   *
 *                                                                         *
 ***************************************************************************/


#ifndef BASE_EXCEPTION_H
#define BASE_EXCEPTION_H

#include <csignal>
#include <string>
#include "BaseClass.h"
#include "FileInfo.h"


using PyObject = struct _object;

/* MACROS FOR THROWING EXCEPTIONS */

/// the macros do NOT mark any message for translation
/// If you want to mark text for translation, use the QT_TRANSLATE_NOOP macro
/// with the context "Exceptions" and the right throwing macro from below (the one ending in T)
/// example:
/// THROWMT(Base::ValueError,QT_TRANSLATE_NOOP("Exceptions","The multiplicity cannot be increased
/// beyond the degree of the B-Spline."));
///
/// N.B.: The QT_TRANSLATE_NOOP macro won't translate your string. It will just allow lupdate to
/// identify that string for translation so that if you ask for a translation (and the translator
/// have provided one) at that time it gets translated (e.g. in the UI before showing the message of
/// the exception).

// NOLINTBEGIN
#ifdef _MSC_VER

#define THROW(exception)                                                                           \
    {                                                                                              \
        exception myexcp;                                                                          \
        myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__);                               \
        throw myexcp;                                                                              \
    }
#define THROWM(exception, message)                                                                 \
    {                                                                                              \
        exception myexcp(message);                                                                 \
        myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__);                               \
        throw myexcp;                                                                              \
    }
#define THROWMF_FILEEXCEPTION(message, filenameorfileinfo)                                         \
    {                                                                                              \
        FileException myexcp(message, filenameorfileinfo);                                         \
        myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__);                               \
        throw myexcp;                                                                              \
    }

#define THROWT(exception)                                                                          \
    {                                                                                              \
        exception myexcp;                                                                          \
        myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__);                               \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }
#define THROWMT(exception, message)                                                                \
    {                                                                                              \
        exception myexcp(message);                                                                 \
        myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__);                               \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }
#define THROWMFT_FILEEXCEPTION(message, filenameorfileinfo)                                        \
    {                                                                                              \
        FileException myexcp(message, filenameorfileinfo);                                         \
        myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__);                               \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }

#elif defined(__GNUC__)

#define THROW(exception)                                                                           \
    {                                                                                              \
        exception myexcp;                                                                          \
        myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__);                       \
        throw myexcp;                                                                              \
    }
#define THROWM(exception, message)                                                                 \
    {                                                                                              \
        exception myexcp(message);                                                                 \
        myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__);                       \
        throw myexcp;                                                                              \
    }
#define THROWMF_FILEEXCEPTION(message, filenameorfileinfo)                                         \
    {                                                                                              \
        FileException myexcp(message, filenameorfileinfo);                                         \
        myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__);                       \
        throw myexcp;                                                                              \
    }

#define THROWT(exception)                                                                          \
    {                                                                                              \
        exception myexcp;                                                                          \
        myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__);                       \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }
#define THROWMT(exception, message)                                                                \
    {                                                                                              \
        exception myexcp(message);                                                                 \
        myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__);                       \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }
#define THROWMFT_FILEEXCEPTION(message, filenameorfileinfo)                                        \
    {                                                                                              \
        FileException myexcp(message, filenameorfileinfo);                                         \
        myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__);                       \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }

#else

#define THROW(exception)                                                                           \
    {                                                                                              \
        exception myexcp;                                                                          \
        myexcp.setDebugInformation(__FILE__, __LINE__, __func__);                                  \
        throw myexcp;                                                                              \
    }
#define THROWM(exception, message)                                                                 \
    {                                                                                              \
        exception myexcp(message);                                                                 \
        myexcp.setDebugInformation(__FILE__, __LINE__, __func__);                                  \
        throw myexcp;                                                                              \
    }
#define THROWMF_FILEEXCEPTION(message, filenameorfileinfo)                                         \
    {                                                                                              \
        FileException myexcp(message, filenameorfileinfo);                                         \
        myexcp.setDebugInformation(__FILE__, __LINE__, __func__);                                  \
        throw myexcp;                                                                              \
    }

#define THROWT(exception)                                                                          \
    {                                                                                              \
        exception myexcp;                                                                          \
        myexcp.setDebugInformation(__FILE__, __LINE__, __func__);                                  \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }
#define THROWMT(exception, message)                                                                \
    {                                                                                              \
        exception myexcp(message);                                                                 \
        myexcp.setDebugInformation(__FILE__, __LINE__, __func__);                                  \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }
#define THROWMFT_FILEEXCEPTION(message, filenameorfileinfo)                                        \
    {                                                                                              \
        FileException myexcp(message, filenameorfileinfo);                                         \
        myexcp.setDebugInformation(__FILE__, __LINE__, __func__);                                  \
        myexcp.setTranslatable(true);                                                              \
        throw myexcp;                                                                              \
    }


#endif

#define FC_THROWM(_exception, _msg)                                                                \
    do {                                                                                           \
        std::stringstream ss;                                                                      \
        ss << _msg;                                                                                \
        THROWM(_exception, ss.str().c_str());                                                      \
    } while (0)
// NOLINTEND

namespace Base
{

class BaseExport Exception: public BaseClass
{
    TYPESYSTEM_HEADER_WITH_OVERRIDE();

public:
    ~Exception() noexcept override = default;

    Exception& operator=(const Exception& inst);
    Exception& operator=(Exception&& inst) noexcept;

    virtual const char* what() const noexcept;

    /// Reports exception. It includes a mechanism to only report an exception once.
    virtual void ReportException() const;

    inline void setMessage(const char* sMessage);
    inline void setMessage(const std::string& sMessage);
    // what may differ from the message given by the user in
    // derived classes
    inline std::string getMessage() const;
    inline std::string getFile() const;
    inline int getLine() const;
    inline std::string getFunction() const;
    inline bool getTranslatable() const;
    inline bool getReported() const
    {
        return _isReported;
    }

    /// setter methods for including debug information
    /// intended to use via macro for autofilling of debugging information
    inline void setDebugInformation(const std::string& file, int line, const std::string& function);

    inline void setTranslatable(bool translatable);

    inline void setReported(bool reported)
    {
        _isReported = reported;
    }

    /// returns a Python dictionary containing the exception data
    PyObject* getPyObject() override;
    /// returns sets the exception data from a Python dictionary
    void setPyObject(PyObject* pydict) override;

    /// returns the corresponding python exception type
    virtual PyObject* getPyExceptionType() const;
    /// Sets the Python error indicator and an error message
    virtual void setPyException() const;

protected:
    /* sMessage may be:
     * - a UI compliant string susceptible to being translated and shown to the user in the UI
     * - a very technical message not intended to be translated or shown to the user in the UI
     * The preferred way of throwing an exception is using the macros above.
     * This way, the file, line, and function are automatically inserted. */
    explicit Exception(const char* sMessage);
    explicit Exception(std::string sMessage);
    Exception();
    Exception(const Exception& inst);
    Exception(Exception&& inst) noexcept;

protected:
    std::string _sErrMsg;
    std::string _file;
    int _line;
    std::string _function;
    bool _isTranslatable;
    mutable bool _isReported;
};


/**
 * The AbortException is thrown if a pending operation was aborted.
 * @author Werner Mayer
 */
class BaseExport AbortException: public Exception
{
    TYPESYSTEM_HEADER_WITH_OVERRIDE();

public:
    /// Construction
    explicit AbortException(const char* sMessage);
    /// Construction
    AbortException();
    AbortException(const AbortException&) = default;
    AbortException(AbortException&&) = default;

    /// Destruction
    ~AbortException() noexcept override = default;
    AbortException& operator=(const AbortException&) = default;
    AbortException& operator=(AbortException&&) = default;

    /// Description of the exception
    const char* what() const noexcept override;
    /// returns the corresponding python exception type
    PyObject* getPyExceptionType() const override;
};

/**
 * The XMLBaseException can be used to indicate any kind of XML related errors.
 * @author Werner Mayer
 */
class BaseExport XMLBaseException: public Exception
{
public:
    /// Construction
    XMLBaseException();
    explicit XMLBaseException(const char* sMessage);
    explicit XMLBaseException(const std::string& sMessage);
    XMLBaseException(const XMLBaseException&) = default;
    XMLBaseException(XMLBaseException&&) = default;

    /// Destruction
    ~XMLBaseException() noexcept override = default;
    XMLBaseException& operator=(const XMLBaseException&) = default;
    XMLBaseException& operator=(XMLBaseException&&) = default;

    PyObject* getPyExceptionType() const override;
};

/**
 * The XMLParseException is thrown if parsing an XML failed.
 * @author Werner Mayer
 */
class BaseExport XMLParseException: public XMLBaseException
{
public:
    /// Construction
    explicit XMLParseException(const char* sMessage);
    /// Construction
    explicit XMLParseException(const std::string& sMessage);
    /// Construction
    XMLParseException();
    XMLParseException(const XMLParseException&) = default;
    XMLParseException(XMLParseException&&) = default;

    /// Destruction
    ~XMLParseException() noexcept override = default;
    XMLParseException& operator=(const XMLParseException&) = default;
    XMLParseException& operator=(XMLParseException&&) = default;

    /// Description of the exception
    const char* what() const noexcept override;
    PyObject* getPyExceptionType() const override;
};

/**
 * The XMLAttributeError is thrown if a requested attribute doesn't exist.
 * @author Werner Mayer
 */
class BaseExport XMLAttributeError: public XMLBaseException
{
public:
    /// Construction
    explicit XMLAttributeError(const char* sMessage);
    /// Construction
    explicit XMLAttributeError(const std::string& sMessage);
    /// Construction
    XMLAttributeError();
    XMLAttributeError(const XMLAttributeError&) = default;
    XMLAttributeError(XMLAttributeError&&) = default;

    /// Destruction
    ~XMLAttributeError() noexcept override = default;
    XMLAttributeError& operator=(const XMLAttributeError&) = default;
    XMLAttributeError& operator=(XMLAttributeError&&) = default;

    /// Description of the exception
    const char* what() const noexcept override;
    PyObject* getPyExceptionType() const override;
};

/** File exception handling class
 * This class is specialized to go with exception thrown in case of File IO Problems.
 * @author Juergen Riegel
 */
class BaseExport FileException: public Exception
{
public:
    /// With massage and file name
    explicit FileException(const char* sMessage, const char* sFileName = nullptr);
    /// With massage and file name
    FileException(const char* sMessage, const FileInfo& File);
    /// standard construction
    FileException();
    FileException(const FileException&) = default;
    FileException(FileException&&) = default;
    /// Destruction
    ~FileException() noexcept override = default;
    /// Assignment operator
    FileException& operator=(const FileException&) = default;
    FileException& operator=(FileException&&) = default;

    /// Description of the exception
    const char* what() const noexcept override;
    /// Report generation
    void ReportException() const override;
    /// Get file name for use with translatable message
    std::string getFileName() const;
    /// returns a Python dictionary containing the exception data
    PyObject* getPyObject() override;
    /// returns sets the exception data from a Python dictionary
    void setPyObject(PyObject* pydict) override;

    PyObject* getPyExceptionType() const override;

protected:
    FileInfo file;
    // necessary   for what() legacy behaviour as it returns a buffer that
    // can not be of a temporary object to be destroyed at end of what()
    std::string _sErrMsgAndFileName;
    void setFileName(const char* sFileName = nullptr);
};

/**
 * The FileSystemError can be used to indicate errors on file system
 * e.g. if renaming of a file failed.
 * @author Werner Mayer
 */
class BaseExport FileSystemError: public Exception
{
public:
    /// Construction
    FileSystemError();
    explicit FileSystemError(const char* sMessage);
    explicit FileSystemError(const std::string& sMessage);
    FileSystemError(const FileSystemError&) = default;
    FileSystemError(FileSystemError&&) = default;
    /// Destruction
    ~FileSystemError() noexcept override = default;
    FileSystemError& operator=(const FileSystemError&) = default;
    FileSystemError& operator=(FileSystemError&&) = default;

    PyObject* getPyExceptionType() const override;
};

/**
 * The BadFormatError can be used to indicate errors in a data structure.
 * @author Werner Mayer
 */
class BaseExport BadFormatError: public Exception
{
public:
    /// Construction
    BadFormatError();
    explicit BadFormatError(const char* sMessage);
    explicit BadFormatError(const std::string& sMessage);
    BadFormatError(const BadFormatError&) = default;
    BadFormatError(BadFormatError&&) = default;
    /// Destruction
    ~BadFormatError() noexcept override = default;
    BadFormatError& operator=(const BadFormatError&) = default;
    BadFormatError& operator=(BadFormatError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The MemoryException is thrown if not enough memory can be allocated.
 * @author Werner Mayer
 */
#if defined(__GNUC__)
// It seems that the calling instance of our new handler expects a bad_alloc exception
class BaseExport MemoryException: public Exception, virtual public std::bad_alloc
#else
class BaseExport MemoryException: public Exception
#endif
{
public:
    /// Construction
    MemoryException();
    /// Construction
    MemoryException(const MemoryException& inst);
    MemoryException(MemoryException&& inst) noexcept;
    /// Destruction
    ~MemoryException() noexcept override = default;
    /// Assignment operator
    MemoryException& operator=(const MemoryException& inst);
    MemoryException& operator=(MemoryException&& inst) noexcept;
#if defined(__GNUC__)
    /// Description of the exception
    const char* what() const noexcept override;
#endif
    PyObject* getPyExceptionType() const override;
};

/**
 * The AccessViolation can be used in an own signal handler.
 * @author Werner Mayer
 */
class BaseExport AccessViolation: public Exception
{
public:
    /// Construction
    AccessViolation();
    explicit AccessViolation(const char* sMessage);
    explicit AccessViolation(const std::string& sMessage);
    AccessViolation(const AccessViolation&) = default;
    AccessViolation(AccessViolation&&) = default;
    /// Destruction
    ~AccessViolation() noexcept override = default;
    AccessViolation& operator=(const AccessViolation&) = default;
    AccessViolation& operator=(AccessViolation&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The AbnormalProgramTermination can be used in an own signal handler.
 * @author Werner Mayer
 */
class BaseExport AbnormalProgramTermination: public Exception
{
public:
    /// Construction
    AbnormalProgramTermination();
    /// Construction
    explicit AbnormalProgramTermination(const char* sMessage);
    explicit AbnormalProgramTermination(const std::string& sMessage);
    AbnormalProgramTermination(const AbnormalProgramTermination&) = default;
    AbnormalProgramTermination(AbnormalProgramTermination&&) = default;
    /// Destruction
    ~AbnormalProgramTermination() noexcept override = default;
    AbnormalProgramTermination& operator=(const AbnormalProgramTermination&) = default;
    AbnormalProgramTermination& operator=(AbnormalProgramTermination&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The UnknownProgramOption can be used to indicate an unknown program option.
 * @author Werner Mayer
 */
class BaseExport UnknownProgramOption: public Exception
{
public:
    /// Construction
    UnknownProgramOption();
    explicit UnknownProgramOption(const char* sMessage);
    explicit UnknownProgramOption(const std::string& sMessage);
    UnknownProgramOption(const UnknownProgramOption&) = default;
    UnknownProgramOption(UnknownProgramOption&&) = default;
    /// Destruction
    ~UnknownProgramOption() noexcept override = default;
    UnknownProgramOption& operator=(const UnknownProgramOption&) = default;
    UnknownProgramOption& operator=(UnknownProgramOption&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The ProgramInformation can be used to show information about the program.
 * @author Werner Mayer
 */
class BaseExport ProgramInformation: public Exception
{
public:
    /// Construction
    ProgramInformation();
    explicit ProgramInformation(const char* sMessage);
    explicit ProgramInformation(const std::string& sMessage);
    ProgramInformation(const ProgramInformation&) = default;
    ProgramInformation(ProgramInformation&&) = default;

    /// Destruction
    ~ProgramInformation() noexcept override = default;
    ProgramInformation& operator=(const ProgramInformation&) = default;
    ProgramInformation& operator=(ProgramInformation&&) = default;
};

/**
 * The TypeError can be used to indicate the usage of a wrong type.
 * @author Werner Mayer
 */
class BaseExport TypeError: public Exception
{
public:
    /// Construction
    TypeError();
    explicit TypeError(const char* sMessage);
    explicit TypeError(const std::string& sMessage);
    TypeError(const TypeError&) = default;
    TypeError(TypeError&&) = default;
    /// Destruction
    ~TypeError() noexcept override = default;
    TypeError& operator=(const TypeError&) = default;
    TypeError& operator=(TypeError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The ValueError can be used to indicate the usage of a wrong value.
 * @author Werner Mayer
 */
class BaseExport ValueError: public Exception
{
public:
    /// Construction
    ValueError();
    explicit ValueError(const char* sMessage);
    explicit ValueError(const std::string& sMessage);
    ValueError(const ValueError&) = default;
    ValueError(ValueError&&) = default;
    /// Destruction
    ~ValueError() noexcept override = default;
    ValueError& operator=(const ValueError&) = default;
    ValueError& operator=(ValueError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The IndexError can be used when a sequence subscript is out of range.
 * @author Werner Mayer
 */
class BaseExport IndexError: public Exception
{
public:
    /// Construction
    IndexError();
    explicit IndexError(const char* sMessage);
    explicit IndexError(const std::string& sMessage);
    IndexError(const IndexError&) = default;
    IndexError(IndexError&&) = default;
    /// Destruction
    ~IndexError() noexcept override = default;
    IndexError& operator=(const IndexError&) = default;
    IndexError& operator=(IndexError&&) = default;
    PyObject* getPyExceptionType() const override;
};

class BaseExport NameError: public Exception
{
public:
    /// Construction
    NameError();
    explicit NameError(const char* sMessage);
    explicit NameError(const std::string& sMessage);
    NameError(const NameError&) = default;
    NameError(NameError&&) = default;
    /// Destruction
    ~NameError() noexcept override = default;
    NameError& operator=(const NameError&) = default;
    NameError& operator=(NameError&&) = default;
    PyObject* getPyExceptionType() const override;
};

class BaseExport ImportError: public Exception
{
public:
    /// Construction
    ImportError();
    explicit ImportError(const char* sMessage);
    explicit ImportError(const std::string& sMessage);
    ImportError(const ImportError&) = default;
    ImportError(ImportError&&) = default;
    /// Destruction
    ~ImportError() noexcept override = default;
    ImportError& operator=(const ImportError&) = default;
    ImportError& operator=(ImportError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The AttributeError can be used to indicate the usage of a wrong value.
 * @author Werner Mayer
 */
class BaseExport AttributeError: public Exception
{
public:
    /// Construction
    AttributeError();
    explicit AttributeError(const char* sMessage);
    explicit AttributeError(const std::string& sMessage);
    AttributeError(const AttributeError&) = default;
    AttributeError(AttributeError&&) = default;
    /// Destruction
    ~AttributeError() noexcept override = default;
    AttributeError& operator=(const AttributeError&) = default;
    AttributeError& operator=(AttributeError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The PropertyError can be used to indicate the usage of a wrong property name or value.
 * @author Mario Passaglia
 */
class BaseExport PropertyError: public AttributeError
{
public:
    /// Construction
    PropertyError();
    explicit PropertyError(const char* sMessage);
    explicit PropertyError(const std::string& sMessage);
    PropertyError(const PropertyError&) = default;
    PropertyError(PropertyError&&) = default;
    /// Destruction
    ~PropertyError() noexcept override = default;
    PropertyError& operator=(const PropertyError&) = default;
    PropertyError& operator=(PropertyError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The RuntimeError can be used to indicate an unknown exception at runtime.
 * @author Werner Mayer
 */
class BaseExport RuntimeError: public Exception
{
public:
    /// Construction
    RuntimeError();
    explicit RuntimeError(const char* sMessage);
    explicit RuntimeError(const std::string& sMessage);
    RuntimeError(const RuntimeError&) = default;
    RuntimeError(RuntimeError&&) = default;
    /// Destruction
    ~RuntimeError() noexcept override = default;
    RuntimeError& operator=(const RuntimeError&) = default;
    RuntimeError& operator=(RuntimeError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The BadGraphError can be used to indicate that a graph is e.g. not a DAG.
 * @author Werner Mayer
 */
class BaseExport BadGraphError: public RuntimeError
{
public:
    /// Construction
    BadGraphError();
    explicit BadGraphError(const char* sMessage);
    explicit BadGraphError(const std::string& sMessage);
    BadGraphError(const BadGraphError&) = default;
    BadGraphError(BadGraphError&&) = default;
    /// Destruction
    ~BadGraphError() noexcept override = default;
    BadGraphError& operator=(const BadGraphError&) = default;
    BadGraphError& operator=(BadGraphError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The NotImplementedError can be used to indicate that an invoked function is not implemented.
 * @author Werner Mayer
 */
class BaseExport NotImplementedError: public Exception
{
public:
    /// Construction
    NotImplementedError();
    explicit NotImplementedError(const char* sMessage);
    explicit NotImplementedError(const std::string& sMessage);
    NotImplementedError(const NotImplementedError&) = default;
    NotImplementedError(NotImplementedError&&) = default;
    /// Destruction
    ~NotImplementedError() noexcept override = default;
    NotImplementedError& operator=(const NotImplementedError&) = default;
    NotImplementedError& operator=(NotImplementedError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The ZeroDivisionError can be used to indicate a division by zero.
 * @author Werner Mayer
 */
class BaseExport ZeroDivisionError: public Exception
{
public:
    /// Construction
    ZeroDivisionError();
    explicit ZeroDivisionError(const char* sMessage);
    explicit ZeroDivisionError(const std::string& sMessage);
    ZeroDivisionError(const ZeroDivisionError&) = default;
    ZeroDivisionError(ZeroDivisionError&&) = default;
    /// Destruction
    ~ZeroDivisionError() noexcept override = default;
    ZeroDivisionError& operator=(const ZeroDivisionError&) = default;
    ZeroDivisionError& operator=(ZeroDivisionError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The ReferenceError can be used to indicate a reference counter has the wrong value.
 * @author Werner Mayer
 */
class BaseExport ReferenceError: public Exception
{
public:
    /// Construction
    ReferenceError();
    explicit ReferenceError(const char* sMessage);
    explicit ReferenceError(const std::string& sMessage);
    ReferenceError(const ReferenceError&) = default;
    ReferenceError(ReferenceError&&) = default;
    /// Destruction
    ~ReferenceError() noexcept override = default;
    ReferenceError& operator=(const ReferenceError&) = default;
    ReferenceError& operator=(ReferenceError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The ExpressionError can be used to indicate erroneous.input
 * to the expression engine.
 * @author Werner Mayer
 */
class BaseExport ExpressionError: public Exception
{
public:
    /// Construction
    ExpressionError();
    explicit ExpressionError(const char* sMessage);
    explicit ExpressionError(const std::string& sMessage);
    ExpressionError(const ExpressionError&) = default;
    ExpressionError(ExpressionError&&) = default;
    /// Destruction
    ~ExpressionError() noexcept override = default;
    ExpressionError& operator=(const ExpressionError&) = default;
    ExpressionError& operator=(ExpressionError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The ParserError can be used to indicate the parsing error.
 * @author Werner Mayer
 */
class BaseExport ParserError: public Exception
{
public:
    /// Construction
    ParserError();
    explicit ParserError(const char* sMessage);
    explicit ParserError(const std::string& sMessage);
    ParserError(const ParserError&) = default;
    ParserError(ParserError&&) = default;
    /// Destruction
    ~ParserError() noexcept override = default;
    ParserError& operator=(const ParserError&) = default;
    ParserError& operator=(ParserError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The UnicodeError can be used to indicate unicode encoding/decoding error.
 * @author Werner Mayer
 */
class BaseExport UnicodeError: public Exception
{
public:
    /// Construction
    UnicodeError();
    explicit UnicodeError(const char* sMessage);
    explicit UnicodeError(const std::string& sMessage);
    UnicodeError(const UnicodeError&) = default;
    UnicodeError(UnicodeError&&) = default;
    /// Destruction
    ~UnicodeError() noexcept override = default;
    UnicodeError& operator=(const UnicodeError&) = default;
    UnicodeError& operator=(UnicodeError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The OverflowError can be used to indicate overflows of numbers.
 * @author Werner Mayer
 */
class BaseExport OverflowError: public Exception
{
public:
    /// Construction
    OverflowError();
    explicit OverflowError(const char* sMessage);
    explicit OverflowError(const std::string& sMessage);
    OverflowError(const OverflowError&) = default;
    OverflowError(OverflowError&&) = default;
    /// Destruction
    ~OverflowError() noexcept override = default;
    OverflowError& operator=(const OverflowError&) = default;
    OverflowError& operator=(OverflowError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The UnderflowError can be used to indicate underflows of numbers.
 * @author Werner Mayer
 */
class BaseExport UnderflowError: public Exception
{
public:
    /// Construction
    UnderflowError();
    explicit UnderflowError(const char* sMessage);
    explicit UnderflowError(const std::string& sMessage);
    UnderflowError(const UnderflowError&) = default;
    UnderflowError(UnderflowError&&) = default;
    /// Destruction
    ~UnderflowError() noexcept override = default;
    UnderflowError& operator=(const UnderflowError&) = default;
    UnderflowError& operator=(UnderflowError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/**
 * The UnitsMismatchError can be used to indicate that quantities with different units are used.
 * @author Werner Mayer
 */
class BaseExport UnitsMismatchError: public Exception
{
public:
    /// Construction
    UnitsMismatchError();
    explicit UnitsMismatchError(const char* sMessage);
    explicit UnitsMismatchError(const std::string& sMessage);
    UnitsMismatchError(const UnitsMismatchError&) = default;
    UnitsMismatchError(UnitsMismatchError&&) = default;
    /// Destruction
    ~UnitsMismatchError() noexcept override = default;
    UnitsMismatchError& operator=(const UnitsMismatchError&) = default;
    UnitsMismatchError& operator=(UnitsMismatchError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/* The CADKernelError can be used to indicate an exception originating in the CAD Kernel
 * allowing to propagate the error messages of, for example, OCC Standard_Failure exception to
 * the FreeCAD application without making the FreeCAD application depend on OCC.
 * @author Abdullah Tahiri
 */
class BaseExport CADKernelError: public Exception
{
public:
    /// Construction
    CADKernelError();
    explicit CADKernelError(const char* sMessage);
    explicit CADKernelError(const std::string& sMessage);
    CADKernelError(const CADKernelError&) = default;
    CADKernelError(CADKernelError&&) = default;
    /// Destruction
    ~CADKernelError() noexcept override = default;
    CADKernelError& operator=(const CADKernelError&) = default;
    CADKernelError& operator=(CADKernelError&&) = default;
    PyObject* getPyExceptionType() const override;
};

/* The RestoreError can be used to try to do a best recovery effort when an error during restoring
 * occurs. The best recovery effort may be to ignore the element altogether or to insert a
 * placeholder depending on where the actual element being restored is used.
 *
 * For example, if it is part of an array (e.g. PropertyList) and the order in the array is
 * relevant, it is better to have a placeholder than to fail to restore the whole array.
 */
class BaseExport RestoreError: public Exception
{
public:
    /// Construction
    RestoreError();
    explicit RestoreError(const char* sMessage);
    explicit RestoreError(const std::string& sMessage);
    RestoreError(const RestoreError&) = default;
    RestoreError(RestoreError&&) = default;
    /// Destruction
    ~RestoreError() noexcept override = default;
    RestoreError& operator=(const RestoreError&) = default;
    RestoreError& operator=(RestoreError&&) = default;
    PyObject* getPyExceptionType() const override;
};


inline void Exception::setMessage(const char* sMessage)
{
    _sErrMsg = sMessage;
}

inline void Exception::setMessage(const std::string& sMessage)
{
    _sErrMsg = sMessage;
}

inline std::string Exception::getMessage() const
{
    return _sErrMsg;
}

inline std::string Exception::getFile() const
{
    return _file;
}

inline int Exception::getLine() const
{
    return _line;
}

inline std::string Exception::getFunction() const
{
    return _function;
}

inline bool Exception::getTranslatable() const
{
    return _isTranslatable;
}

inline void
Exception::setDebugInformation(const std::string& file, int line, const std::string& function)
{
    _file = file;
    _line = line;
    _function = function;
}

inline void Exception::setTranslatable(bool translatable)
{
    _isTranslatable = translatable;
}

#if defined(__GNUC__) && defined(FC_OS_LINUX)
class SignalException
{
public:
    SignalException();
    ~SignalException();

private:
    static void throw_signal(int signum);

private:
    // clang-format off
    struct sigaction new_action {}, old_action {};
    bool ok {false};
    // clang-format on
};
#endif

}  // namespace Base

#endif  // BASE_EXCEPTION_H
