// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// error.hpp
//
// 状態管理システム関連のエラー処理
//
// このファイルでは、数値状態管理システムに関連するエラー処理機能の追加インターフェースを提供します。
// common.hppで定義された例外クラスと状態エラー処理機能を利用して、より特化したエラー処理を実装します。
//
// 主な機能:
// - 特殊なエラー検出ユーティリティ
// - 追加のエラー処理ポリシー

#ifndef CALX_ERROR_HPP
#define CALX_ERROR_HPP

#include <math/core/numeric_state.hpp>
#include <math/core/common.hpp>
#include <string>
#include <stdexcept>
#include <concepts>

namespace calx {

    // エラー処理ユーティリティ

    // ゼロ除算チェック
    template<typename T>
    inline void check_division_by_zero(T divisor, const std::string& message = "Division by zero") {
        if (divisor == T(0)) {
            const auto& options = global_state_error_options();
            switch (options.divide_by_zero_error_mode) {
            case StateErrorMode::Exception:
                throw DivideByZeroError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // 特殊値は呼び出し側で処理
                break;
            }
        }
    }

    // NaNチェック
    template<std::floating_point T>
    inline void check_nan(const T& value, const std::string& message = "Operation resulted in NaN") {
        if (std::isnan(value)) {
            const auto& options = global_state_error_options();
            switch (options.nan_error_mode) {
            case StateErrorMode::Exception:
                throw NaNError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // 特殊値は呼び出し側で処理
                break;
            }
        }
    }

    // 無限大チェック
    template<std::floating_point T>
    inline void check_infinity(const T& value, const std::string& message = "Operation resulted in infinity") {
        if (std::isinf(value)) {
            const auto& options = global_state_error_options();
            switch (options.infinity_error_mode) {
            case StateErrorMode::Exception:
                throw InfinityError(message, value > 0);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // 特殊値は呼び出し側で処理
                break;
            }
        }
    }

    // オーバーフローチェック
    inline void check_overflow(bool overflow_condition, const std::string& message = "Numeric overflow") {
        if (overflow_condition) {
            const auto& options = global_state_error_options();
            switch (options.overflow_error_mode) {
            case StateErrorMode::Exception:
                throw OverflowError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // 特殊値は呼び出し側で処理
                break;
            }
        }
    }

    // アンダーフローチェック
    inline void check_underflow(bool underflow_condition, const std::string& message = "Numeric underflow") {
        if (underflow_condition) {
            const auto& options = global_state_error_options();
            switch (options.underflow_error_mode) {
            case StateErrorMode::Exception:
                throw UnderflowError(message);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // 特殊値は呼び出し側で処理
                break;
            }
        }
    }

    // 発散チェック
    inline void check_divergence(bool divergence_condition,
        DivergenceDetail detail = DivergenceDetail::Diverging,
        const std::string& message = "Algorithm diverged") {
        if (divergence_condition) {
            const auto& options = global_state_error_options();
            switch (options.divergence_error_mode) {
            case StateErrorMode::Exception:
                throw DivergenceStateError(message, detail);
            case StateErrorMode::ReturnSpecial:
            case StateErrorMode::Silent:
                // 特殊値は呼び出し側で処理
                break;
            }
        }
    }

    // エラー原因と状態を結合したチェック
    inline void check_error_and_state(NumericError error, NumericState state,
        const std::string& message = "Numeric error occurred") {
        const auto& options = global_state_error_options();

        // エラー原因に基づくチェック
        if (error != NumericError::None) {
            switch (error) {
            case NumericError::DivideByZero:
                if (options.divide_by_zero_error_mode == StateErrorMode::Exception) {
                    throw DivideByZeroError(message);
                }
                break;
            case NumericError::NaNPropagation:
            case NumericError::ExplicitNaN:
                if (options.nan_error_mode == StateErrorMode::Exception) {
                    throw NaNError(message);
                }
                break;
            case NumericError::ConversionError:
            case NumericError::OutOfRangeInput:
                if (options.overflow_error_mode == StateErrorMode::Exception) {
                    throw OverflowError(message);
                }
                break;
            case NumericError::DivergenceError:
                if (options.divergence_error_mode == StateErrorMode::Exception) {
                    throw DivergenceStateError(message);
                }
                break;
            default:
                // その他のエラーは特に処理しない
                break;
            }
        }

        // 状態に基づくチェック
        if (NumericStateTraits::isNaN(state) && options.nan_error_mode == StateErrorMode::Exception) {
            throw NaNError(message);
        }
        if (NumericStateTraits::isInfinite(state)) {
            if (options.infinity_error_mode == StateErrorMode::Exception) {
                bool is_positive = (state == NumericState::PositiveInfinity);
                throw InfinityError(message, is_positive);
            }
        }
        if (state == NumericState::Overflow && options.overflow_error_mode == StateErrorMode::Exception) {
            throw OverflowError(message);
        }
        if (state == NumericState::Underflow && options.underflow_error_mode == StateErrorMode::Exception) {
            throw UnderflowError(message);
        }
        if (NumericStateTraits::isDivergent(state) && options.divergence_error_mode == StateErrorMode::Exception) {
            throw DivergenceStateError(message);
        }
    }

    // 追加: 状態とエラーの一貫性を検証するユーティリティ
    inline void validate_state_and_error(NumericState& state, NumericError& error) {
        // エラーがあるのに状態が正常な場合、エラーに対応する状態を設定
        if (error != NumericError::None && state == NumericState::Normal) {
            state = NumericStateTraits::errorToState(error);
        }

        // 状態が特殊なのにエラーがない場合、状態に対応するデフォルトのエラーを設定
        if (NumericStateTraits::requiresErrorCode(state) && error == NumericError::None) {
            error = NumericStateTraits::stateToDefaultError(state);
        }
    }

    // 追加: 状態とエラーを一貫して設定するヘルパー
    inline void set_state_and_error(NumericState& state_dest, NumericError& error_dest,
        NumericState state_src, NumericError error_src) {
        state_dest = state_src;
        error_dest = error_src;
        validate_state_and_error(state_dest, error_dest);
    }

} // namespace calx

#endif // CALX_ERROR_HPP