use super::linear::extract_numeric;
use crate::Result;
use crate::blocks::conversion::base::ConversionBlock;
use crate::types::DecodedValue;
use alloc::vec::Vec;

/// General table lookup: either interpolated or nearest neighbour.
/// `values` must be `[key0, val0, key1, val1, …]`.
pub fn lookup_table(values: &[f64], raw: f64, interp: bool) -> Option<f64> {
    let len = values.len();
    if len < 4 || len % 2 != 0 {
        return None;
    }
    let n = len / 2;
    let mut table = Vec::with_capacity(n);
    for i in 0..n {
        table.push((values[2 * i], values[2 * i + 1]));
    }
    if raw <= table[0].0 {
        return Some(table[0].1);
    }
    if raw >= table[n - 1].0 {
        return Some(table[n - 1].1);
    }
    for i in 0..(n - 1) {
        let (k0, v0) = table[i];
        let (k1, v1) = table[i + 1];
        if raw >= k0 && raw <= k1 {
            if interp {
                let t = (raw - k0) / (k1 - k0);
                return Some(v0 + t * (v1 - v0));
            } else {
                let d0 = raw - k0;
                let d1 = k1 - raw;
                return Some(if d1 < d0 { v1 } else { v0 });
            }
        }
    }
    None
}

pub fn apply_table_lookup(
    block: &ConversionBlock,
    value: DecodedValue,
    interp: bool,
) -> Result<DecodedValue> {
    if let Some(raw) = extract_numeric(&value) {
        let phys = lookup_table(&block.values, raw, interp).unwrap_or(raw);
        Ok(DecodedValue::Float(phys))
    } else {
        Ok(value)
    }
}

pub fn apply_range_lookup(block: &ConversionBlock, value: DecodedValue) -> Result<DecodedValue> {
    if let Some(raw) = extract_numeric(&value) {
        let inclusive_upper = matches!(
            value,
            DecodedValue::UnsignedInteger(_) | DecodedValue::SignedInteger(_)
        );
        let v = &block.values;
        if v.len() < 4 || (v.len() - 1) % 3 != 0 {
            return Ok(DecodedValue::Float(raw));
        }
        let n = (v.len() - 1) / 3;
        let default = v[3 * n];
        for i in 0..n {
            let key_min = v[3 * i];
            let key_max = v[3 * i + 1];
            let phys = v[3 * i + 2];
            if inclusive_upper {
                if raw >= key_min && raw <= key_max {
                    return Ok(DecodedValue::Float(phys));
                }
            } else if raw >= key_min && raw < key_max {
                return Ok(DecodedValue::Float(phys));
            }
        }
        Ok(DecodedValue::Float(default))
    } else {
        Ok(value)
    }
}
