/*
 * // Copyright (c) Radzivon Bartoshyk 3/2026. All rights reserved.
 * //
 * // Redistribution and use in source and binary forms, with or without modification,
 * // are permitted provided that the following conditions are met:
 * //
 * // 1.  Redistributions of source code must retain the above copyright notice, this
 * // list of conditions and the following disclaimer.
 * //
 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
 * // this list of conditions and the following disclaimer in the documentation
 * // and/or other materials provided with the distribution.
 * //
 * // 3.  Neither the name of the copyright holder nor the names of its
 * // contributors may be used to endorse or promote products derived from
 * // this software without specific prior written permission.
 * //
 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
use crate::zpk::{bilinear_zpk, lp2bp_zpk, lp2hp_zpk, lp2lp_zpk};
use crate::{FilterType, ImpliirError, ZpkFilter};
use pxfm::f_tanpi;

pub(crate) fn filter_zpk(
    order: usize,
    rp: f64,
    ftype: FilterType,
    freqs: [f64; 2],
    fs: f64,
    filter: fn(usize, f64) -> ZpkFilter,
) -> Result<ZpkFilter, ImpliirError> {
    if order == 0 {
        return Err(ImpliirError::InvalidOrder);
    }
    let nyquist = fs / 2.0;

    let warped: Vec<f64> = match ftype {
        FilterType::Lowpass | FilterType::Highpass => {
            let f = freqs[0];
            if f <= 0.0 || f >= nyquist {
                return Err(ImpliirError::InvalidFrequency);
            }
            let wn = f / nyquist; // normalise to 0..1
            vec![4.0 * f_tanpi(wn * 0.5)] // 2 * 2.0 * tan(pi*wn/2)
        }
        FilterType::Bandpass => {
            let (lo, hi) = (freqs[0], freqs[1]);
            if lo <= 0.0 || hi >= nyquist || lo >= hi {
                return Err(ImpliirError::InvalidBandFrequencies);
            }
            let wn_lo = lo / nyquist;
            let wn_hi = hi / nyquist;
            vec![4.0 * f_tanpi(wn_lo * 0.5), 4.0 * f_tanpi(wn_hi * 0.5)]
        }
    };

    let ZpkFilter { z, p, k } = filter(order, rp);

    let (z, p, k) = match ftype {
        FilterType::Lowpass => lp2lp_zpk(z, p, k, warped[0]),
        FilterType::Highpass => lp2hp_zpk(z, p, k, warped[0]),
        FilterType::Bandpass => {
            let bw = warped[1] - warped[0];
            let wo = (warped[0] * warped[1]).sqrt();
            lp2bp_zpk(z, p, k, wo, bw)
        }
    };

    let (z, p, k) = bilinear_zpk(z, p, k, 2.0);

    Ok(ZpkFilter { z, p, k })
}
