//! Audio encoding utilities for the LiveSpeech SDK

use base64::{engine::general_purpose::STANDARD, Engine};

/// Encode audio data to base64
pub fn encode_to_base64(data: &[u8]) -> String {
    STANDARD.encode(data)
}

/// Decode base64 to audio data
pub fn decode_from_base64(base64: &str) -> Result<Vec<u8>, base64::DecodeError> {
    STANDARD.decode(base64)
}

/// Convert f32 audio samples to i16 PCM
pub fn float32_to_int16(samples: &[f32]) -> Vec<i16> {
    samples
        .iter()
        .map(|&sample| {
            let clamped = sample.clamp(-1.0, 1.0);
            if clamped < 0.0 {
                (clamped * 32768.0) as i16
            } else {
                (clamped * 32767.0) as i16
            }
        })
        .collect()
}

/// Convert i16 PCM to f32 audio samples
pub fn int16_to_float32(samples: &[i16]) -> Vec<f32> {
    samples
        .iter()
        .map(|&sample| {
            if sample < 0 {
                sample as f32 / 32768.0
            } else {
                sample as f32 / 32767.0
            }
        })
        .collect()
}

/// Convert i16 samples to bytes (little-endian)
pub fn int16_to_bytes(samples: &[i16]) -> Vec<u8> {
    let mut bytes = Vec::with_capacity(samples.len() * 2);
    for &sample in samples {
        bytes.extend_from_slice(&sample.to_le_bytes());
    }
    bytes
}

/// Convert bytes to i16 samples (little-endian)
pub fn bytes_to_int16(bytes: &[u8]) -> Vec<i16> {
    bytes
        .chunks_exact(2)
        .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]]))
        .collect()
}

/// Create a WAV header for PCM audio
pub fn create_wav_header(data_length: u32, sample_rate: u32, channels: u16, bit_depth: u16) -> Vec<u8> {
    let byte_rate = sample_rate * channels as u32 * bit_depth as u32 / 8;
    let block_align = channels * bit_depth / 8;
    
    let mut header = Vec::with_capacity(44);
    
    // RIFF header
    header.extend_from_slice(b"RIFF");
    header.extend_from_slice(&(36 + data_length).to_le_bytes());
    header.extend_from_slice(b"WAVE");
    
    // fmt sub-chunk
    header.extend_from_slice(b"fmt ");
    header.extend_from_slice(&16u32.to_le_bytes()); // Sub-chunk size
    header.extend_from_slice(&1u16.to_le_bytes()); // Audio format (1 = PCM)
    header.extend_from_slice(&channels.to_le_bytes());
    header.extend_from_slice(&sample_rate.to_le_bytes());
    header.extend_from_slice(&byte_rate.to_le_bytes());
    header.extend_from_slice(&block_align.to_le_bytes());
    header.extend_from_slice(&bit_depth.to_le_bytes());
    
    // data sub-chunk
    header.extend_from_slice(b"data");
    header.extend_from_slice(&data_length.to_le_bytes());
    
    header
}

/// Wrap PCM data in a WAV container
pub fn wrap_pcm_in_wav(pcm_data: &[u8], sample_rate: u32, channels: u16, bit_depth: u16) -> Vec<u8> {
    let header = create_wav_header(pcm_data.len() as u32, sample_rate, channels, bit_depth);
    let mut wav = header;
    wav.extend_from_slice(pcm_data);
    wav
}

/// Audio encoder/decoder
#[derive(Debug, Clone)]
pub struct AudioEncoder {
    pub sample_rate: u32,
    pub channels: u16,
    pub bit_depth: u16,
}

impl Default for AudioEncoder {
    fn default() -> Self {
        Self {
            sample_rate: 16000,
            channels: 1,
            bit_depth: 16,
        }
    }
}

impl AudioEncoder {
    /// Create a new audio encoder with default settings
    pub fn new() -> Self {
        Self::default()
    }

    /// Create with custom settings
    pub fn with_settings(sample_rate: u32, channels: u16, bit_depth: u16) -> Self {
        Self {
            sample_rate,
            channels,
            bit_depth,
        }
    }

    /// Encode audio bytes to base64
    pub fn encode(&self, data: &[u8]) -> String {
        encode_to_base64(data)
    }

    /// Decode base64 to audio bytes
    pub fn decode(&self, base64: &str) -> Result<Vec<u8>, base64::DecodeError> {
        decode_from_base64(base64)
    }

    /// Convert f32 samples to bytes for transmission
    pub fn from_float32(&self, samples: &[f32]) -> Vec<u8> {
        let int16 = float32_to_int16(samples);
        int16_to_bytes(&int16)
    }

    /// Convert received bytes to f32 samples
    pub fn to_float32(&self, bytes: &[u8]) -> Vec<f32> {
        let int16 = bytes_to_int16(bytes);
        int16_to_float32(&int16)
    }

    /// Wrap PCM data in WAV format
    pub fn wrap_wav(&self, pcm_data: &[u8]) -> Vec<u8> {
        wrap_pcm_in_wav(pcm_data, self.sample_rate, self.channels, self.bit_depth)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_base64_roundtrip() {
        let data = vec![1, 2, 3, 4, 5];
        let encoded = encode_to_base64(&data);
        let decoded = decode_from_base64(&encoded).unwrap();
        assert_eq!(data, decoded);
    }

    #[test]
    fn test_float_int_roundtrip() {
        let floats = vec![0.0, 0.5, -0.5, 1.0, -1.0];
        let int16 = float32_to_int16(&floats);
        let back = int16_to_float32(&int16);
        
        for (orig, converted) in floats.iter().zip(back.iter()) {
            assert!((orig - converted).abs() < 0.001);
        }
    }

    #[test]
    fn test_wav_header() {
        let header = create_wav_header(1000, 16000, 1, 16);
        assert_eq!(header.len(), 44);
        assert_eq!(&header[0..4], b"RIFF");
        assert_eq!(&header[8..12], b"WAVE");
    }
}
