//! Range scaling processing for normalizing float values to specific ranges.
//!
//! This module provides processing that linearly scale input float values from a specified
//! input range to normalized output ranges. These processing are commonly used to normalize
//! sensor data or other continuous values for FEAGI processing.

use std::any::Any;
use std::fmt::{Display, Formatter};
use std::time::Instant;
use feagi_data_structures::data::{Percentage, SignedPercentage};
use feagi_data_structures::FeagiDataError;
use feagi_data_structures::wrapped_io_data::{WrappedIOData, WrappedIOType};
use crate::data_pipeline::pipeline_stage::PipelineStage;

/// A stream processor that linearly scales input float values to the range [0, 1].
///
/// This processor takes float values within a specified input range [lower_bound, upper_bound]
/// and maps them linearly to the normalized range [0, 1]. Values outside the given bounds
/// are clamped to the bounds before scaling.
#[derive(Debug, Clone)]
pub struct LinearScaleToPercentageStage {
    previous_value: WrappedIOData,
    lower: f32,
    upper: f32,
}

impl Display for LinearScaleToPercentageStage {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "LinearScaleTo0And1(lower_bound={:?},upper_bound={:?},prev_val={:?})", self.lower, self.upper,  self.previous_value)
    }
}

impl PipelineStage for LinearScaleToPercentageStage {
    fn get_input_data_type(&self) -> WrappedIOType {
        WrappedIOType::F32
    }

    fn get_output_data_type(&self) -> WrappedIOType {
        WrappedIOType::Percentage
    }

    fn get_most_recent_output(&self) -> &WrappedIOData {
        &self.previous_value
    }

    fn process_new_input(&mut self, value: &WrappedIOData, _: Instant) -> Result<&WrappedIOData, FeagiDataError> {
        let float_result = f32::try_from(value)?;
        let percentage = Percentage::new_from_linear_interp(float_result, &(self.lower..self.upper))?;
        self.previous_value = WrappedIOData::Percentage(percentage);
        Ok(&self.previous_value)
    }

    fn clone_box(&self) -> Box<dyn PipelineStage> {
        Box::new(self.clone())
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}

impl LinearScaleToPercentageStage {
    /// Creates a new LinearScaleTo0And1 processor.
    ///
    /// # Arguments
    /// * `lower_bound` - The minimum value of the input range
    /// * `upper_bound` - The maximum value of the input range (must be > lower_bound)
    /// * `initial_value` - The initial value to store in the processor (must be within bounds)
    ///
    /// # Returns
    /// * `Ok(LinearScaleTo0And1)` - A new processor instance
    /// * `Err(FeagiDataError)` - If parameters are invalid (NaN, infinite, or out of bounds)
    pub fn new(lower_bound: f32, upper_bound: f32, initial_value: Percentage) -> Result<Self, FeagiDataError> {
        if lower_bound.is_nan() || lower_bound.is_infinite() {
            return Err(FeagiDataError::BadParameters(format!("Given lower bound float {} is not valid!", lower_bound)));
        }
        if upper_bound.is_nan() || upper_bound.is_infinite() {
            return Err(FeagiDataError::BadParameters(format!("Given upper bound float {} is not valid!", upper_bound)));
        }

        Ok(LinearScaleToPercentageStage {
            previous_value: WrappedIOData::Percentage(initial_value),
            lower: lower_bound,
            upper: upper_bound,
        })
    }
}


/// A stream processor that linearly scales input float values to the range [-1, 1].
///
/// This processor takes float values within a specified input range [lower_bound, upper_bound]
/// and maps them linearly to the normalized range [-1, 1]. Values outside the given bounds
/// are clamped to the bounds before scaling.
#[derive(Debug, Clone)]
pub struct LinearScaleToSignedPercentageStage {
    previous_value: WrappedIOData,
    lower: f32,
    upper: f32,
}

impl Display for LinearScaleToSignedPercentageStage {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "LinearScaleToM1And1(lower_bound={:?},upper_bound={:?},prev_val={:?})", self.lower, self.upper,  self.previous_value)
    }
}

impl PipelineStage for LinearScaleToSignedPercentageStage {
    fn get_input_data_type(&self) -> WrappedIOType {
        WrappedIOType::F32
    }

    fn get_output_data_type(&self) -> WrappedIOType {
        WrappedIOType::SignedPercentage
    }

    fn get_most_recent_output(&self) -> &WrappedIOData {
        &self.previous_value
    }

    fn process_new_input(&mut self, value: &WrappedIOData, _: Instant) -> Result<&WrappedIOData, FeagiDataError> {
        let float_result = f32::try_from(value)?;

        let percentage = SignedPercentage::new_from_linear_interp(float_result, &(self.lower..self.upper))?;
        self.previous_value = WrappedIOData::SignedPercentage(percentage);
        Ok(&self.previous_value)
    }

    fn clone_box(&self) -> Box<dyn PipelineStage> {
        Box::new(self.clone())
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}

impl LinearScaleToSignedPercentageStage {
    /// Creates a new LinearScaleToM1And1 processor.
    ///
    /// # Arguments
    /// * `lower_bound` - The minimum value of the input range
    /// * `upper_bound` - The maximum value of the input range (must be > lower_bound)
    /// * `initial_value` - The initial value to store in the processor (must be within bounds)
    ///
    /// # Returns
    /// * `Ok(LinearScaleToM1And1)` - A new processor instance
    /// * `Err(FeagiDataError)` - If parameters are invalid (NaN, infinite, or out of bounds)
    pub fn new(lower_bound: f32, upper_bound: f32, initial_value: SignedPercentage) -> Result<Self, FeagiDataError> {
        // TODO why arent we using Range?
        if lower_bound.is_nan() || lower_bound.is_infinite() {
            return Err(FeagiDataError::BadParameters(format!("Given lower bound float {} is not valid!", lower_bound)));
        }
        if upper_bound.is_nan() || upper_bound.is_infinite() {
            return Err(FeagiDataError::BadParameters(format!("Given upper bound float {} is not valid!", upper_bound)));
        }
        if upper_bound <= lower_bound {
            return Err(FeagiDataError::BadParameters(format!("Upper bound float {} must be greater than lower bound {}!!", upper_bound, lower_bound)));
        }
        Ok(LinearScaleToSignedPercentageStage {
            previous_value: WrappedIOData::SignedPercentage(initial_value),
            lower: lower_bound,
            upper: upper_bound,
        })
    }
}