/*
  Copyright (C) 2016-2026 Dan Casarin (https://www.kfrlib.com)
  This file is part of KFR

  KFR is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 2 of the License, or
  (at your option) any later version.

  KFR 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with KFR.

  If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
  Buying a commercial license is mandatory as soon as you develop commercial activities without
  disclosing the source code of your own applications.
  See https://www.kfrlib.com for details.
 */
#pragma once

#include "../base/expression.hpp"
#include "../math/sqrt.hpp"
#include "../simd/operators.hpp"

namespace kfr
{
inline namespace KFR_ARCH_NAME
{

namespace intr
{

template <typename T>
KFR_INTRINSIC T weight_a_unnorm(T f)
{
    const T f2  = pow2(f);
    const T nom = pow2(12200) * pow4(f);
    const T den = (f2 + pow2(20.6)) * (sqrt((f2 + pow2(107.7)) * (f2 + pow2(737.9)))) * (f2 + pow2(12200));
    return nom / den;
}

template <typename T>
constexpr inline T weight_a_gain = T(1.25889662908332766733);

template <typename T>
KFR_INTRINSIC T aweighting(T f)
{
    return weight_a_unnorm(f) * weight_a_gain<subtype<T>>;
}

template <typename T>
KFR_INTRINSIC T weight_b_unnorm(T f)
{
    const T f2  = pow2(f);
    const T nom = pow2(12200) * pow3(f);
    const T den = (f2 + pow2(20.6)) * (sqrt((f2 + pow2(158.5)))) * (f2 + pow2(12200));

    return nom / den;
}

template <typename T>
constexpr inline T weight_b_gain = T(1.01971824783723263863);

template <typename T>
KFR_INTRINSIC T bweighting(T f)
{
    return weight_b_unnorm(f) * weight_b_gain<subtype<T>>;
}

template <typename T>
KFR_INTRINSIC T weight_c_unnorm(T f)
{
    const T f2  = pow2(f);
    const T nom = pow2(12200) * f2;
    const T den = (f2 + pow2(20.6)) * (f2 + pow2(12200));

    return nom / den;
}

template <typename T>
constexpr inline T weight_c_gain = T(1.00714583514109112805);

template <typename T>
KFR_INTRINSIC T cweighting(T f)
{
    return weight_c_unnorm(f) * weight_c_gain<subtype<T>>;
}
} // namespace intr
KFR_I_FN(aweighting)
KFR_I_FN(bweighting)
KFR_I_FN(cweighting)

/**
 * @brief A-weighting filter response for a scalar frequency.
 *
 * Returns the A-weighting gain (linear magnitude, not decibels) for the given
 * frequency @p x in Hz. The result is normalized so that the response at
 * 1 kHz is unity (0 dB). Use this on spectrum magnitudes (gains); convert to
 * dB afterwards if a decibel-scaled weighting is required.
 *
 * @tparam T1 Numeric scalar type.
 * @param x Frequency in Hz.
 * @return A-weighting gain (linear) at @p x.
 */
template <numeric T1>
KFR_INTRINSIC T1 aweighting(const T1& x)
{
    return intr::aweighting(x);
}

/**
 * @brief A-weighting filter response as an expression.
 *
 * Builds an expression that applies the A-weighting gain (linear magnitude,
 * not decibels) element-wise to its input. Intended for use on spectrum
 * magnitudes (gains); the input is interpreted as frequency in Hz.
 *
 * @tparam E1 Input expression type.
 * @param x Input expression of frequencies in Hz.
 * @return Expression producing A-weighting gains (linear) element-wise.
 */
template <expression_argument E1>
KFR_INTRINSIC expression_function<fn::aweighting, E1> aweighting(E1&& x)
{
    return { fn::aweighting(), std::forward<E1>(x) };
}

/**
 * @brief B-weighting filter response for a scalar frequency.
 *
 * Returns the B-weighting gain (linear magnitude, not decibels) for the given
 * frequency @p x in Hz. The result is normalized so that the response at
 * 1 kHz is unity (0 dB). Use this on spectrum magnitudes (gains); convert to
 * dB afterwards if a decibel-scaled weighting is required.
 *
 * @tparam T1 Numeric scalar type.
 * @param x Frequency in Hz.
 * @return B-weighting gain (linear) at @p x.
 */
template <numeric T1>
KFR_INTRINSIC T1 bweighting(const T1& x)
{
    return intr::bweighting(x);
}

/**
 * @brief B-weighting filter response as an expression.
 *
 * Builds an expression that applies the B-weighting gain (linear magnitude,
 * not decibels) element-wise to its input. Intended for use on spectrum
 * magnitudes (gains); the input is interpreted as frequency in Hz.
 *
 * @tparam E1 Input expression type.
 * @param x Input expression of frequencies in Hz.
 * @return Expression producing B-weighting gains (linear) element-wise.
 */
template <expression_argument E1>
KFR_INTRINSIC expression_function<fn::bweighting, E1> bweighting(E1&& x)
{
    return { fn::bweighting(), std::forward<E1>(x) };
}

/**
 * @brief C-weighting filter response for a scalar frequency.
 *
 * Returns the C-weighting gain (linear magnitude, not decibels) for the given
 * frequency @p x in Hz. The result is normalized so that the response at
 * 1 kHz is unity (0 dB). Use this on spectrum magnitudes (gains); convert to
 * dB afterwards if a decibel-scaled weighting is required.
 *
 * @tparam T1 Numeric scalar type.
 * @param x Frequency in Hz.
 * @return C-weighting gain (linear) at @p x.
 */
template <numeric T1>
KFR_INTRINSIC T1 cweighting(const T1& x)
{
    return intr::cweighting(x);
}

/**
 * @brief C-weighting filter response as an expression.
 *
 * Builds an expression that applies the C-weighting gain (linear magnitude,
 * not decibels) element-wise to its input. Intended for use on spectrum
 * magnitudes (gains); the input is interpreted as frequency in Hz.
 *
 * @tparam E1 Input expression type.
 * @param x Input expression of frequencies in Hz.
 * @return Expression producing C-weighting gains (linear) element-wise.
 */
template <expression_argument E1>
KFR_INTRINSIC expression_function<fn::cweighting, E1> cweighting(E1&& x)
{
    return { fn::cweighting(), std::forward<E1>(x) };
}
} // namespace KFR_ARCH_NAME
} // namespace kfr
