/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_EXCEPTIONS_H
#define _GCTL_EXCEPTIONS_H

#include <string>
#include <iostream>
#include <exception>
#include <stdexcept>
#include <type_traits>

/**
 * define error symbols
 */
#define GCTL_ERROR_ERROR -1
#define GCTL_WARNING_ERROR -2
#define GCTL_MESSAGE_ERROR -3

#if defined _WINDOWS || __WIN32__
#include <io.h>
#include <process.h>
#include <windows.h>
#else
#include <unistd.h>
#endif

#if defined _WINDOWS || __WIN32__

/**
 * @brief      规格化显示错误及帮助信息
 *
 * @param      err_msg     错误信息
 * @param      err_type    错误类型 必须为三个宏定义之一
 * @param      extra_info  与错误信息有关的额外信息 等于0时不显示
 * @param      help_info   与错误有关的帮助信息 等于0时不显示
 * @param      orgin       报告错误的源头 一般为相应的函数声明
 *
 */
#define GCTL_ShowWhatError(err_msg, err_type, extra_info, help_info, orgin) \
if (err_type == GCTL_ERROR_ERROR) \
    SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED); \
    std::cerr << "Error ==> " << err_msg; \
else if (err_type == GCTL_WARNING_ERROR) \
    SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_YELLOW); \
    std::cerr << "Warning ==> " << err_msg; \
else if (err_type == GCTL_MESSAGE_ERROR) \
    SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE); \
    std::cerr << "Message ==> " << err_msg; \
else \
    SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED); \
    std::cerr << "Error ==> " << err_msg; \
if (extra_info != 0) \
    std::cerr << " " << extra_info; \
std::cerr << std::endl; \
if (help_info != 0) \
    SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); \
    std::cerr << "Help ==> " << help_info; \
if (orgin != 0) \
    SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN); \
    std::cerr << "From ==> " << orgin; \
std::cout << std::endl; \
SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), 7);

#else

#define GCTL_ShowWhatError(err_msg, err_type, extra_info, help_info, orgin) \
if (err_type == GCTL_ERROR_ERROR) \
    std::cerr << "\033[1m\033[31mError ==>\033[0m " << err_msg; \
else if (err_type == GCTL_WARNING_ERROR) \
    std::cerr << "\033[1m\033[33mWarning ==>\033[0m " << err_msg; \
else if (err_type == GCTL_MESSAGE_ERROR) \
    std::cerr << "\033[1m\033[34mMessage ==>\033[0m " << err_msg; \
else \
    std::cerr << "\033[1m\033[31mError ==>\033[0m " << err_msg; \
if (extra_info != 0) \
    std::cerr << " " << extra_info; \
std::cerr << std::endl; \
if (help_info != 0) \
    std::cerr << "\033[1m\033[32mHelp ==>\033[0m " << help_info << std::endl; \
if (orgin != 0) \
    std::cerr << "\033[1m\033[32mFrom ==>\033[0m " << orgin << std::endl;

#endif

namespace gctl
{
    class runtime_error : public std::runtime_error
    {
    public:
        runtime_error() : std::runtime_error("GCTL: Unexpected runtime error."){}
        explicit runtime_error(std::string estr) : std::runtime_error(("GCTL: Unexpected runtime error. "+estr).c_str()){}
    };

    class range_error : public std::range_error
    {
    public:
        range_error() : std::range_error("GCTL: Invalid range detected."){}
        explicit range_error(std::string estr) : std::range_error(("GCTL: Invalid range detected. "+estr).c_str()){}
    };

    class overflow_error : public std::overflow_error
    {
    public:
        overflow_error() : std::overflow_error("GCTL: Overflow error."){}
        explicit overflow_error(std::string estr) : std::overflow_error(("GCTL: Overflow error. "+estr).c_str()){}
    };

    class underflow_error : public std::underflow_error
    {
    public:
        underflow_error() : std::underflow_error("GCTL: Underflow error."){}
        explicit underflow_error(std::string estr) : std::underflow_error(("GCTL: Underflow error. "+estr).c_str()){}
    };


    class logic_error : public std::logic_error
    {
    public:
        logic_error() : std::logic_error("GCTL: Logic error."){}
        explicit logic_error(std::string estr) : std::logic_error(("GCTL: Logic error. "+estr).c_str()){}
    };

    class domain_error : public std::domain_error
    {
    public:
        domain_error() : std::domain_error("GCTL: Domain error."){}
        explicit domain_error(std::string estr) : std::domain_error(("GCTL: Domain error. "+estr).c_str()){}
    };

    class invalid_argument : public std::invalid_argument
    {
    public:
        invalid_argument() : std::invalid_argument("GCTL: Invalid argument."){}
        explicit invalid_argument(std::string estr) : std::invalid_argument(("GCTL: Invalid argument. "+estr).c_str()){}
    };

    class length_error : public std::length_error
    {
    public:
        length_error() : std::length_error("GCTL: Length error."){}
        explicit length_error(std::string estr) : std::length_error(("GCTL: Length error. "+estr).c_str()){}
    };

    class out_of_range : public std::out_of_range
    {
    public:
        out_of_range() : std::out_of_range("GCTL: Out of range."){}
        explicit out_of_range(std::string estr) : std::out_of_range(("GCTL: Out of range. "+estr).c_str()){}
    };
};

#endif // _GCTL_EXCEPTIONS_H