#include "mp3_player.hpp"
#include <SDL2/SDL.h>
#include <mpg123.h>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <cmath>
#include <fstream>
#include <complex>

namespace AudioAnalysis {

// Simple FFT implementation for audio analysis
class SimpleFFT {
public:
    static std::vector<std::complex<double>> fft(std::vector<std::complex<double>> x) {
        int N = x.size();
        if (N <= 1) return x;
        
        // Divide
        std::vector<std::complex<double>> even, odd;
        for (int i = 0; i < N; i += 2) even.push_back(x[i]);
        for (int i = 1; i < N; i += 2) odd.push_back(x[i]);
        
        // Conquer
        auto even_fft = fft(even);
        auto odd_fft = fft(odd);
        
        // Combine
        std::vector<std::complex<double>> result(N);
        for (int k = 0; k < N/2; ++k) {
            auto t = std::polar(1.0, -2 * M_PI * k / N) * odd_fft[k];
            result[k] = even_fft[k] + t;
            result[k + N/2] = even_fft[k] - t;
        }
        return result;
    }
};

MP3Player::MP3Player() {
    // Initialize SDL audio
    if (SDL_Init(SDL_INIT_AUDIO) < 0) {
        std::cerr << "Failed to initialize SDL: " << SDL_GetError() << std::endl;
        return;
    }
    
    // Initialize mpg123
    if (mpg123_init() != MPG123_OK) {
        std::cerr << "Failed to initialize mpg123" << std::endl;
        return;
    }
    
    // Initialize energy history for beat detection
    energy_history.reserve(100);
}

MP3Player::~MP3Player() {
    stop();
    if (playback_thread.joinable()) playback_thread.join();
    if (analysis_thread.joinable()) analysis_thread.join();
    
    SDL_CloseAudio();
    SDL_Quit();
    mpg123_exit();
}

bool MP3Player::load_mp3_file(const std::string& filename) {
    mpg123_handle* mh = mpg123_new(nullptr, nullptr);
    if (!mh) {
        std::cerr << "Failed to create mpg123 handle" << std::endl;
        return false;
    }
    
    if (mpg123_open(mh, filename.c_str()) != MPG123_OK) {
        std::cerr << "Failed to open MP3 file: " << filename << std::endl;
        mpg123_delete(mh);
        return false;
    }
    
    long rate;
    int channels, encoding;
    if (mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK) {
        std::cerr << "Failed to get MP3 format" << std::endl;
        mpg123_delete(mh);
        return false;
    }
    
    std::cout << "MP3 Format: " << rate << "Hz, " << channels << " channels, encoding=" << encoding << std::endl;
    
    // Force format to something we can work with
    mpg123_format_none(mh);
    if (mpg123_format(mh, rate, channels, MPG123_ENC_SIGNED_16) != MPG123_OK) {
        std::cerr << "Failed to set MP3 format" << std::endl;
        mpg123_delete(mh);
        return false;
    }
    
    // Get file length
    off_t length = mpg123_length(mh);
    if (length == MPG123_ERR) {
        std::cerr << "Warning: Could not determine MP3 length" << std::endl;
        duration_seconds.store(0.0);
    } else {
        duration_seconds.store(static_cast<double>(length) / rate);
        std::cout << "MP3 Duration: " << duration_seconds.load() << " seconds" << std::endl;
    }
    
    // Read entire file into buffer
    std::vector<short> raw_buffer;
    size_t buffer_size = mpg123_outblock(mh);
    std::vector<unsigned char> temp_buffer(buffer_size);
    size_t done;
    int result;
    
    std::cout << "Reading MP3 data..." << std::endl;
    size_t total_bytes = 0;
    
    while ((result = mpg123_read(mh, temp_buffer.data(), buffer_size, &done)) == MPG123_OK) {
        if (done > 0) {
            short* samples = reinterpret_cast<short*>(temp_buffer.data());
            size_t sample_count = done / sizeof(short);
            raw_buffer.insert(raw_buffer.end(), samples, samples + sample_count);
            total_bytes += done;
        }
    }
    
    // Handle the case where we reach EOF
    if (result == MPG123_DONE && done > 0) {
        short* samples = reinterpret_cast<short*>(temp_buffer.data());
        size_t sample_count = done / sizeof(short);
        raw_buffer.insert(raw_buffer.end(), samples, samples + sample_count);
        total_bytes += done;
    }
    
    mpg123_delete(mh);
    
    if (raw_buffer.empty()) {
        std::cerr << "No audio data read from MP3 file" << std::endl;
        return false;
    }
    
    std::cout << "Read " << total_bytes << " bytes, " << raw_buffer.size() << " samples" << std::endl;
    
    // Convert to float and store
    std::lock_guard<std::mutex> lock(audio_mutex);
    current_buffer.data.clear();
    current_buffer.data.reserve(raw_buffer.size());
    
    for (short sample : raw_buffer) {
        current_buffer.data.push_back(static_cast<float>(sample) / 32768.0f);
    }
    
    current_buffer.size = current_buffer.data.size();
    current_buffer.channels = channels;
    current_buffer.sample_rate = rate;
    current_filename = filename;
    
    std::cout << "Converted to " << current_buffer.size << " float samples" << std::endl;
    std::cout << "Sample rate: " << current_buffer.sample_rate << "Hz, Channels: " << current_buffer.channels << std::endl;
    
    // Playback length follows decoded PCM (may be shorter than header estimate).
    {
        double buffer_duration = static_cast<double>(current_buffer.size)
            / (static_cast<double>(current_buffer.sample_rate)
               * static_cast<double>(std::max<size_t>(1, current_buffer.channels)));
        if (duration_seconds.load() <= 0.0)
            duration_seconds.store(buffer_duration);
        else
            duration_seconds.store(std::min(duration_seconds.load(), buffer_duration));
    }
    
    return true;
}

bool MP3Player::load(const std::string& filename) {
    stop();
    return load_mp3_file(filename);
}

bool MP3Player::play() {
    if (current_buffer.data.empty()) return false;
    
    if (is_paused.load()) {
        is_paused.store(false);
        return true;
    }
    
    if (is_playing.load()) return true;
    
    should_stop.store(false);
    is_playing.store(true);
    
    // Start playback thread
    if (playback_thread.joinable()) playback_thread.join();
    playback_thread = std::thread(&MP3Player::audio_playback_loop, this);
    
    // Start analysis thread
    if (analysis_thread.joinable()) analysis_thread.join();
    analysis_thread = std::thread(&MP3Player::audio_analysis_loop, this);
    
    // Start continuous monitoring thread
    if (analysis_params.enable_continuous_monitoring) {
        if (continuous_monitor_thread.joinable()) continuous_monitor_thread.join();
        continuous_monitor_thread = std::thread(&MP3Player::continuous_monitoring_loop, this);
    }
    
    return true;
}

bool MP3Player::pause() {
    is_paused.store(!is_paused.load());
    return true;
}

bool MP3Player::stop() {
    std::cout << "Stopping MP3 player..." << std::endl;
    
    should_stop.store(true);
    is_playing.store(false);
    is_paused.store(false);
    position_seconds.store(0.0);
    
    // Wait for threads to finish with a timeout
    const auto timeout = std::chrono::seconds(2);
    
    if (playback_thread.joinable()) {
        auto start = std::chrono::steady_clock::now();
        while (playback_thread.joinable() && 
               (std::chrono::steady_clock::now() - start) < timeout) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        if (playback_thread.joinable()) {
            std::cout << "Force joining playback thread..." << std::endl;
            playback_thread.join();
        }
    }
    
    if (analysis_thread.joinable()) {
        auto start = std::chrono::steady_clock::now();
        while (analysis_thread.joinable() && 
               (std::chrono::steady_clock::now() - start) < timeout) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        if (analysis_thread.joinable()) {
            std::cout << "Force joining analysis thread..." << std::endl;
            analysis_thread.join();
        }
    }
    
    if (continuous_monitor_thread.joinable()) {
        auto start = std::chrono::steady_clock::now();
        while (continuous_monitor_thread.joinable() && 
               (std::chrono::steady_clock::now() - start) < timeout) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
        if (continuous_monitor_thread.joinable()) {
            std::cout << "Force joining continuous monitor thread..." << std::endl;
            continuous_monitor_thread.join();
        }
    }
    
    std::cout << "MP3 player stopped." << std::endl;
    return true;
}

bool MP3Player::is_loaded() const {
    return !current_buffer.data.empty();
}

bool MP3Player::seek(double seconds) {
    if (!is_loaded()) return false;
    
    double max_pos = duration_seconds.load();
    seconds = std::clamp(seconds, 0.0, max_pos);
    position_seconds.store(seconds);
    
    return true;
}

void MP3Player::set_volume(double volume) {
    current_volume = std::clamp(volume, 0.0, 1.0);
}

double MP3Player::get_volume() const {
    return current_volume;
}

void MP3Player::set_beat_detection_params(const BeatDetectionParams& params) {
    beat_params = params;
}

void MP3Player::add_event_callback(AudioEventCallback callback) {
    std::lock_guard<std::mutex> lock(callback_mutex);
    event_callbacks.push_back(callback);
}

void MP3Player::clear_event_callbacks() {
    std::lock_guard<std::mutex> lock(callback_mutex);
    event_callbacks.clear();
}

AudioFeatures MP3Player::get_current_features() const {
    std::lock_guard<std::mutex> lock(features_mutex);
    return current_features;
}

void MP3Player::audio_playback_loop() {
    SDL_AudioSpec wanted_spec, obtained_spec;
    wanted_spec.freq = current_buffer.sample_rate;
    wanted_spec.format = AUDIO_F32SYS;
    wanted_spec.channels = current_buffer.channels;
    wanted_spec.samples = 1024;
    wanted_spec.callback = nullptr; // We'll use SDL_QueueAudio
    
    SDL_AudioDeviceID device = SDL_OpenAudioDevice(nullptr, 0, &wanted_spec, &obtained_spec, 0);
    if (device == 0) {
        std::cerr << "Failed to open audio device: " << SDL_GetError() << std::endl;
        return;
    }
    
    std::cout << "Audio opened: " << obtained_spec.freq << "Hz, " 
              << static_cast<int>(obtained_spec.channels) << " channels, format=" 
              << obtained_spec.format << std::endl;
    
    SDL_PauseAudioDevice(device, 0); // Start playing
    
    const size_t samples_per_chunk = 1024;
    const size_t bytes_per_sample = sizeof(float);
    const size_t chunk_size_samples = samples_per_chunk * current_buffer.channels;
    
    size_t current_sample_pos = 0;
    const size_t total_samples = current_buffer.data.size();
    
    auto start_time = std::chrono::steady_clock::now();
    auto last_time = start_time;
    
    while (is_playing.load() && !should_stop.load()) {
        if (is_paused.load()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            start_time = std::chrono::steady_clock::now(); // Reset timing when unpaused
            last_time = start_time;
            continue;
        }
        
        if (current_sample_pos >= total_samples) {
            if (!loop_enabled.load()) {
                current_sample_pos = total_samples;
                position_seconds.store(static_cast<double>(total_samples)
                    / (current_buffer.sample_rate * std::max<size_t>(1, current_buffer.channels)));
                is_playing.store(false);
                std::cout << "Audio playback finished" << std::endl;
                break;
            }
            current_sample_pos = 0;
            position_seconds.store(0.0);
            start_time = std::chrono::steady_clock::now();
            std::cout << "Audio looped back to beginning" << std::endl;
        }
        
        // Calculate how much audio SDL has queued
        Uint32 queued_bytes = SDL_GetQueuedAudioSize(device);
        Uint32 queued_samples = queued_bytes / bytes_per_sample / current_buffer.channels;
        
        // Only queue more audio if the buffer is getting low
        if (queued_samples < samples_per_chunk * 2) {
            size_t samples_to_queue = std::min(chunk_size_samples, total_samples - current_sample_pos);
            
            if (samples_to_queue > 0) {
                // Create chunk with volume applied
                std::vector<float> chunk;
                chunk.reserve(samples_to_queue);
                
                for (size_t i = 0; i < samples_to_queue; ++i) {
                    chunk.push_back(current_buffer.data[current_sample_pos + i] * current_volume);
                }
                
                // Queue the audio
                if (SDL_QueueAudio(device, chunk.data(), samples_to_queue * bytes_per_sample) != 0) {
                    std::cerr << "Failed to queue audio: " << SDL_GetError() << std::endl;
                    break;
                }
                
                current_sample_pos += samples_to_queue;
            }
        }
        
        // Update position based on how much audio has been processed
        auto now = std::chrono::steady_clock::now();
        auto elapsed_seconds = std::chrono::duration<double>(now - start_time).count();
        
        // Calculate position based on samples processed minus what's still queued
        size_t played_samples = current_sample_pos - queued_samples;
        double calculated_position = static_cast<double>(played_samples) / 
                                   (current_buffer.sample_rate * current_buffer.channels);
        
        // Use the more accurate of time-based or sample-based position
        double time_based_position = elapsed_seconds;
        position_seconds.store(std::min(calculated_position, time_based_position));
        
        // Small delay to prevent busy waiting
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
        last_time = now;
    }
    
    // Clear any remaining audio and close device
    SDL_ClearQueuedAudio(device);
    SDL_CloseAudioDevice(device);
}

void MP3Player::audio_analysis_loop() {
    const size_t frame_size = beat_params.window_size;
    const size_t hop_size = beat_params.hop_size;
    
    while (is_playing.load() && !should_stop.load()) {
        if (is_paused.load() || should_stop.load()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            continue;
        }
        
        size_t current_pos = static_cast<size_t>(position_seconds.load() * current_buffer.sample_rate * current_buffer.channels);
        
        if (current_pos + frame_size >= current_buffer.data.size()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            continue;
        }
        
        // Process audio frame
        process_audio_frame(current_buffer.data.data() + current_pos, frame_size);
        
        // Use smaller sleep chunks to be more responsive to stop signal
        int sleep_duration = static_cast<int>(hop_size * 1000 / current_buffer.sample_rate);
        for (int i = 0; i < sleep_duration && !should_stop.load(); i += 10) {
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }
    }
}

void MP3Player::process_audio_frame(const float* samples, size_t frame_size) {
    AudioFeatures features = analyze_audio_features(samples, frame_size);
    
    {
        std::lock_guard<std::mutex> lock(features_mutex);
        current_features = features;
    }
    
    // Trigger regular events
    trigger_events(features);
    
    // Check and trigger specific events
    check_and_trigger_specific_events(features);
}

AudioFeatures MP3Player::analyze_audio_features(const float* samples, size_t frame_size) {
    AudioFeatures features;
    
    // Calculate energy
    double energy = 0.0;
    for (size_t i = 0; i < frame_size; ++i) {
        energy += samples[i] * samples[i];
    }
    energy /= frame_size;
    features.energy = std::sqrt(energy);
    
    // Compute FFT for frequency analysis
    features.spectrum = compute_fft(samples, frame_size);
    
    // Calculate spectral centroid
    features.spectral_centroid = calculate_spectral_centroid(features.spectrum);
    
    // Extended analysis
    if (analysis_params.enable_freq_bands) {
        features.freq_bands = analyze_frequency_bands(features.spectrum, current_buffer.sample_rate);
    }
    
    if (analysis_params.enable_harmonic_analysis) {
        features.harmonic_ratio = calculate_harmonic_ratio(features.spectrum);
    }
    
    // Calculate additional features
    features.dynamics = calculate_dynamics(samples, frame_size);
    features.zero_crossing_rate = calculate_zero_crossing_rate(samples, frame_size);
    features.spectral_rolloff = calculate_spectral_rolloff(features.spectrum);
    features.onset_strength = calculate_onset_strength(features.spectrum, prev_spectrum);
    features.silence_detected = detect_silence(samples, frame_size);
    
    if (analysis_params.enable_mfcc) {
        calculate_mfcc(features.spectrum, features.mfcc);
    }
    
    // Beat detection
    features.beat_detected = detect_beat(features.spectrum, features.energy);
    
    // Calculate tempo
    features.tempo = calculate_tempo();
    
    // Beat strength (simplified)
    features.beat_strength = features.beat_detected ? 
        std::min(1.0, features.energy * 2.0) : 0.0;
    
    // Store spectrum for next onset detection
    prev_spectrum = features.spectrum;
    
    return features;
}

std::vector<double> MP3Player::compute_fft(const float* samples, size_t size) {
    // Create complex input for FFT
    std::vector<std::complex<double>> complex_input;
    complex_input.reserve(size);
    
    for (size_t i = 0; i < size; ++i) {
        complex_input.emplace_back(samples[i], 0.0);
    }
    
    // Pad to next power of 2 for efficiency
    size_t fft_size = 1;
    while (fft_size < size) fft_size <<= 1;
    
    complex_input.resize(fft_size, std::complex<double>(0.0, 0.0));
    
    // Compute FFT
    auto fft_result = AudioAnalysis::SimpleFFT::fft(complex_input);
    
    // Convert to magnitude spectrum
    std::vector<double> spectrum;
    spectrum.reserve(fft_size / 2);
    
    for (size_t i = 0; i < fft_size / 2; ++i) {
        spectrum.push_back(std::abs(fft_result[i]));
    }
    
    return spectrum;
}

double MP3Player::calculate_spectral_centroid(const std::vector<double>& spectrum) {
    double weighted_sum = 0.0;
    double magnitude_sum = 0.0;
    
    for (size_t i = 0; i < spectrum.size(); ++i) {
        weighted_sum += i * spectrum[i];
        magnitude_sum += spectrum[i];
    }
    
    return magnitude_sum > 0 ? weighted_sum / magnitude_sum : 0.0;
}

bool MP3Player::detect_beat(const std::vector<double>& /* spectrum */, double energy) {
    // Simple beat detection based on energy flux
    energy_history.push_back(energy);
    
    // Keep only recent history
    if (energy_history.size() > 20) {
        energy_history.erase(energy_history.begin());
    }
    
    if (energy_history.size() < 5) return false;
    
    // Calculate average energy of recent history
    double avg_energy = std::accumulate(energy_history.end() - 5, energy_history.end(), 0.0) / 5.0;
    
    // Beat detected if current energy significantly exceeds recent average
    bool beat = energy > avg_energy * (1.0 + beat_params.sensitivity);
    
    if (beat) {
        beat_times.push_back(position_seconds.load());
        // Keep only recent beats for tempo calculation
        if (beat_times.size() > 20) {
            beat_times.erase(beat_times.begin());
        }
    }
    
    return beat;
}

double MP3Player::calculate_tempo() {
    if (beat_times.size() < 4) return current_tempo;
    
    // Calculate intervals between beats
    std::vector<double> intervals;
    for (size_t i = 1; i < beat_times.size(); ++i) {
        intervals.push_back(beat_times[i] - beat_times[i-1]);
    }
    
    // Find median interval
    std::sort(intervals.begin(), intervals.end());
    double median_interval = intervals[intervals.size() / 2];
    
    // Convert to BPM
    double calculated_tempo = 60.0 / median_interval;
    
    // Clamp to reasonable range
    calculated_tempo = std::clamp(calculated_tempo, beat_params.min_bpm, beat_params.max_bpm);
    
    // Smooth tempo changes
    current_tempo = current_tempo * 0.8 + calculated_tempo * 0.2;
    
    return current_tempo;
}

void MP3Player::trigger_events(const AudioFeatures& features) {
    std::lock_guard<std::mutex> lock(callback_mutex);
    for (const auto& callback : event_callbacks) {
        callback(features, position_seconds.load());
    }
}

FrequencyBands MP3Player::analyze_frequency_bands(const std::vector<double>& spectrum, int sample_rate) {
    FrequencyBands bands = {0, 0, 0, 0, 0};
    
    if (spectrum.empty()) return bands;
    
    double freq_resolution = static_cast<double>(sample_rate) / (2.0 * spectrum.size());
    
    for (size_t i = 0; i < spectrum.size(); ++i) {
        double freq = i * freq_resolution;
        double magnitude = spectrum[i];
        
        if (freq >= 20 && freq < 250) {
            bands.bass += magnitude;
        } else if (freq >= 250 && freq < 500) {
            bands.low_mid += magnitude;
        } else if (freq >= 500 && freq < 2000) {
            bands.mid += magnitude;
        } else if (freq >= 2000 && freq < 4000) {
            bands.high_mid += magnitude;
        } else if (freq >= 4000) {
            bands.treble += magnitude;
        }
    }
    
    // Normalize bands
    double total = bands.bass + bands.low_mid + bands.mid + bands.high_mid + bands.treble;
    if (total > 0) {
        bands.bass /= total;
        bands.low_mid /= total;
        bands.mid /= total;
        bands.high_mid /= total;
        bands.treble /= total;
    }
    
    return bands;
}

double MP3Player::calculate_harmonic_ratio(const std::vector<double>& spectrum) {
    if (spectrum.size() < 10) return 0.0;
    
    // Find the fundamental frequency (peak)
    auto max_it = std::max_element(spectrum.begin() + 1, spectrum.end() - 1);
    size_t fundamental_bin = std::distance(spectrum.begin(), max_it);
    
    if (fundamental_bin == 0) return 0.0;
    
    // Calculate harmonic energy vs total energy
    double harmonic_energy = 0.0;
    double total_energy = 0.0;
    
    for (size_t i = 1; i < spectrum.size(); ++i) {
        total_energy += spectrum[i];
        
        // Check if this bin is near a harmonic
        for (int harmonic = 1; harmonic <= 5; ++harmonic) {
            size_t harmonic_bin = fundamental_bin * harmonic;
            if (harmonic_bin < spectrum.size() && 
                std::abs(static_cast<int>(i) - static_cast<int>(harmonic_bin)) <= 2) {
                harmonic_energy += spectrum[i];
                break;
            }
        }
    }
    
    return total_energy > 0 ? harmonic_energy / total_energy : 0.0;
}

double MP3Player::calculate_dynamics(const float* samples, size_t frame_size) {
    if (frame_size == 0) return 0.0;
    
    float min_val = samples[0];
    float max_val = samples[0];
    
    for (size_t i = 1; i < frame_size; ++i) {
        min_val = std::min(min_val, samples[i]);
        max_val = std::max(max_val, samples[i]);
    }
    
    return static_cast<double>(max_val - min_val);
}

double MP3Player::calculate_zero_crossing_rate(const float* samples, size_t frame_size) {
    if (frame_size < 2) return 0.0;
    
    int crossings = 0;
    for (size_t i = 1; i < frame_size; ++i) {
        if ((samples[i-1] >= 0) != (samples[i] >= 0)) {
            crossings++;
        }
    }
    
    return static_cast<double>(crossings) / frame_size;
}

double MP3Player::calculate_spectral_rolloff(const std::vector<double>& spectrum) {
    if (spectrum.empty()) return 0.0;
    
    double total_energy = 0.0;
    for (double mag : spectrum) {
        total_energy += mag;
    }
    
    if (total_energy == 0.0) return 0.0;
    
    double threshold = 0.85 * total_energy;
    double cumulative_energy = 0.0;
    
    for (size_t i = 0; i < spectrum.size(); ++i) {
        cumulative_energy += spectrum[i];
        if (cumulative_energy >= threshold) {
            return static_cast<double>(i) / spectrum.size();
        }
    }
    
    return 1.0;
}

void MP3Player::calculate_mfcc(const std::vector<double>& spectrum, double* mfcc_out) {
    // Simplified MFCC calculation (normally would use mel filter banks)
    for (int i = 0; i < 13; ++i) {
        mfcc_out[i] = 0.0;
        
        if (!spectrum.empty()) {
            // Simple approximation: take log of different frequency ranges
            size_t start = (i * spectrum.size()) / 13;
            size_t end = ((i + 1) * spectrum.size()) / 13;
            
            double sum = 0.0;
            for (size_t j = start; j < end && j < spectrum.size(); ++j) {
                sum += spectrum[j];
            }
            
            mfcc_out[i] = sum > 0 ? std::log(1.0 + sum) : 0.0;
        }
    }
}

double MP3Player::calculate_onset_strength(const std::vector<double>& spectrum, const std::vector<double>& prev_spectrum) {
    if (spectrum.size() != prev_spectrum.size() || spectrum.empty()) {
        return 0.0;
    }
    
    double onset_strength = 0.0;
    for (size_t i = 0; i < spectrum.size(); ++i) {
        double diff = spectrum[i] - prev_spectrum[i];
        if (diff > 0) {
            onset_strength += diff;
        }
    }
    
    return onset_strength / spectrum.size();
}

bool MP3Player::detect_silence(const float* samples, size_t frame_size) {
    if (frame_size == 0) return true;
    
    double rms = 0.0;
    for (size_t i = 0; i < frame_size; ++i) {
        rms += samples[i] * samples[i];
    }
    rms = std::sqrt(rms / frame_size);
    
    return rms < 0.01; // Threshold for silence
}

// Event management methods
void MP3Player::add_continuous_callback(ContinuousCallback callback) {
    std::lock_guard<std::mutex> lock(callback_mutex);
    continuous_callbacks.push_back(callback);
}

void MP3Player::add_specific_event_callback(SpecificEventCallback callback) {
    std::lock_guard<std::mutex> lock(callback_mutex);
    specific_event_callbacks.push_back(callback);
}

void MP3Player::clear_continuous_callbacks() {
    std::lock_guard<std::mutex> lock(callback_mutex);
    continuous_callbacks.clear();
}

void MP3Player::clear_specific_event_callbacks() {
    std::lock_guard<std::mutex> lock(callback_mutex);
    specific_event_callbacks.clear();
}

void MP3Player::clear_all_callbacks() {
    std::lock_guard<std::mutex> lock(callback_mutex);
    event_callbacks.clear();
    continuous_callbacks.clear();
    specific_event_callbacks.clear();
}

void MP3Player::set_analysis_params(const AnalysisParams& params) {
    analysis_params = params;
    
    // Update beat detection params for compatibility
    beat_params.sensitivity = params.beat_sensitivity;
    beat_params.window_size = params.window_size;
    beat_params.hop_size = params.hop_size;
    beat_params.min_bpm = params.min_bpm;
    beat_params.max_bpm = params.max_bpm;
}

// Real-time parameter access
FrequencyBands MP3Player::get_current_frequency_bands() const {
    std::lock_guard<std::mutex> lock(features_mutex);
    return current_features.freq_bands;
}

double MP3Player::get_current_dynamics() const {
    std::lock_guard<std::mutex> lock(features_mutex);
    return current_features.dynamics;
}

double MP3Player::get_current_harmonic_ratio() const {
    std::lock_guard<std::mutex> lock(features_mutex);
    return current_features.harmonic_ratio;
}

// Continuous monitoring loop
void MP3Player::continuous_monitoring_loop() {
    last_continuous_update = std::chrono::steady_clock::now();
    
    while (is_playing.load() && !should_stop.load()) {
        if (is_paused.load() || should_stop.load()) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            continue;
        }
        
        auto now = std::chrono::steady_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_continuous_update).count();
        
        if (elapsed >= analysis_params.continuous_update_rate_ms) {
            // Trigger continuous callbacks
            {
                std::lock_guard<std::mutex> lock(callback_mutex);
                for (const auto& callback : continuous_callbacks) {
                    AudioFeatures features;
                    {
                        std::lock_guard<std::mutex> features_lock(features_mutex);
                        features = current_features;
                    }
                    callback(features, position_seconds.load());
                }
            }
            
            last_continuous_update = now;
        }
        
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

// Check and trigger specific events
void MP3Player::check_and_trigger_specific_events(const AudioFeatures& features) {
    auto now = std::chrono::steady_clock::now();
    double timestamp = position_seconds.load();
    
    std::lock_guard<std::mutex> lock(callback_mutex);
    
    // Energy spike events
    if (features.energy > analysis_params.energy_spike_threshold) {
        AudioEvent event;
        event.type = EventType::ENERGY_SPIKE;
        event.timestamp = timestamp;
        event.intensity = features.energy;
        event.features = features;
        event.parameters["energy_level"] = features.energy;
        
        for (const auto& callback : specific_event_callbacks) {
            callback(event);
        }
    }
    
    // Frequency modulation events
    double freq_change = std::abs(features.freq_bands.bass - prev_freq_bands.bass) +
                        std::abs(features.freq_bands.mid - prev_freq_bands.mid) +
                        std::abs(features.freq_bands.treble - prev_freq_bands.treble);
    
    if (freq_change > analysis_params.freq_modulation_threshold) {
        AudioEvent event;
        event.type = EventType::FREQ_MODULATION;
        event.timestamp = timestamp;
        event.intensity = freq_change;
        event.features = features;
        event.parameters["bass_change"] = features.freq_bands.bass - prev_freq_bands.bass;
        event.parameters["mid_change"] = features.freq_bands.mid - prev_freq_bands.mid;
        event.parameters["treble_change"] = features.freq_bands.treble - prev_freq_bands.treble;
        
        for (const auto& callback : specific_event_callbacks) {
            callback(event);
        }
    }
    
    // Harmonic change events
    if (std::abs(features.harmonic_ratio - prev_harmonic_ratio) > 0.2) {
        AudioEvent event;
        event.type = EventType::HARMONIC_CHANGE;
        event.timestamp = timestamp;
        event.intensity = std::abs(features.harmonic_ratio - prev_harmonic_ratio);
        event.features = features;
        event.parameters["harmonic_ratio"] = features.harmonic_ratio;
        event.parameters["harmonic_change"] = features.harmonic_ratio - prev_harmonic_ratio;
        
        for (const auto& callback : specific_event_callbacks) {
            callback(event);
        }
    }
    
    // Dynamic range change events
    if (std::abs(features.dynamics - prev_dynamics) > analysis_params.dynamic_change_threshold) {
        AudioEvent event;
        event.type = EventType::DYNAMIC_CHANGE;
        event.timestamp = timestamp;
        event.intensity = std::abs(features.dynamics - prev_dynamics);
        event.features = features;
        event.parameters["dynamics"] = features.dynamics;
        event.parameters["dynamics_change"] = features.dynamics - prev_dynamics;
        
        for (const auto& callback : specific_event_callbacks) {
            callback(event);
        }
    }
    
    // Onset events
    if (features.onset_strength > 0.1) {
        AudioEvent event;
        event.type = EventType::ONSET;
        event.timestamp = timestamp;
        event.intensity = features.onset_strength;
        event.features = features;
        event.parameters["onset_strength"] = features.onset_strength;
        
        for (const auto& callback : specific_event_callbacks) {
            callback(event);
        }
    }
    
    // Silence events
    if (features.silence_detected) {
        AudioEvent event;
        event.type = EventType::SILENCE;
        event.timestamp = timestamp;
        event.intensity = 1.0 - features.energy; // Inverse of energy
        event.features = features;
        
        for (const auto& callback : specific_event_callbacks) {
            callback(event);
        }
    }
    
    // Update previous values for change detection
    prev_freq_bands = features.freq_bands;
    prev_harmonic_ratio = features.harmonic_ratio;
    prev_dynamics = features.dynamics;
}

void MP3Player::reset_offline_analysis_state() {
    energy_history.clear();
    beat_times.clear();
    prev_spectrum.clear();
    prev_harmonic_ratio = 0.0;
    prev_dynamics = 0.0;
    prev_freq_bands = {0, 0, 0, 0, 0};
    current_tempo = 0.0;
    position_seconds.store(0.0);
}

void MP3Player::collect_offline_events(const AudioFeatures& features, double timestamp,
                                       OfflineAnalysisResult& result,
                                       double& last_energy_spike, double& last_onset,
                                       double& last_freq_mod, double& last_harmonic,
                                       double& last_dynamic,
                                       bool& in_silence, double& silence_start) {
    auto push_event = [&](EventType type, double intensity) {
        OfflineEventMarker m;
        m.time = timestamp;
        m.type = type;
        m.intensity = intensity;
        result.events.push_back(m);
    };

    if (features.energy > analysis_params.energy_spike_threshold
        && timestamp - last_energy_spike > 0.35) {
        push_event(EventType::ENERGY_SPIKE, features.energy);
        last_energy_spike = timestamp;
    }

    if (features.onset_strength > 0.12 && timestamp - last_onset > 0.20) {
        push_event(EventType::ONSET, features.onset_strength);
        last_onset = timestamp;
    }

    double freq_change = std::abs(features.freq_bands.bass - prev_freq_bands.bass)
                       + std::abs(features.freq_bands.mid - prev_freq_bands.mid)
                       + std::abs(features.freq_bands.treble - prev_freq_bands.treble);
    if (freq_change > analysis_params.freq_modulation_threshold
        && timestamp - last_freq_mod > 0.45) {
        push_event(EventType::FREQ_MODULATION, freq_change);
        last_freq_mod = timestamp;
    }

    if (std::abs(features.harmonic_ratio - prev_harmonic_ratio) > 0.22
        && timestamp - last_harmonic > 0.55) {
        push_event(EventType::HARMONIC_CHANGE,
                   std::abs(features.harmonic_ratio - prev_harmonic_ratio));
        last_harmonic = timestamp;
    }

    if (std::abs(features.dynamics - prev_dynamics) > analysis_params.dynamic_change_threshold
        && timestamp - last_dynamic > 0.40) {
        push_event(EventType::DYNAMIC_CHANGE, std::abs(features.dynamics - prev_dynamics));
        last_dynamic = timestamp;
    }

    if (features.silence_detected) {
        if (!in_silence) {
            in_silence = true;
            silence_start = timestamp;
        }
    } else if (in_silence) {
        if (timestamp - silence_start > 0.35)
            push_event(EventType::SILENCE, timestamp - silence_start);
        in_silence = false;
    }

    prev_freq_bands = features.freq_bands;
    prev_harmonic_ratio = features.harmonic_ratio;
    prev_dynamics = features.dynamics;
}

void MP3Player::build_offline_phrases(OfflineAnalysisResult& result) {
    result.phrases.clear();
    if (result.envelope_times.size() < 4) return;

    const double min_phrase = 2.5;
    const double silence_thresh = 0.045;
    const double run_needed = 0.45;
    double phrase_start = result.envelope_times.front();
    double energy_sum = 0.0;
    int energy_count = 0;
    double low_run = 0.0;
    double prev_t = phrase_start;

    auto flush_phrase = [&](double end_t) {
        if (end_t - phrase_start < min_phrase) return;
        OfflinePhraseSegment seg;
        seg.start = phrase_start;
        seg.end = end_t;
        seg.mean_energy = energy_count > 0 ? energy_sum / double(energy_count) : 0.0;
        for (double bt : result.beat_times) {
            if (bt >= seg.start && bt < seg.end) seg.beat_count++;
        }
        result.phrases.push_back(seg);
    };

    for (size_t i = 0; i < result.envelope_times.size(); ++i) {
        double t = result.envelope_times[i];
        double e = result.envelope_energy[i];
        double dt = std::max(0.001, t - prev_t);
        prev_t = t;
        energy_sum += e;
        energy_count++;

        if (e < silence_thresh)
            low_run += dt;
        else {
            if (low_run >= run_needed) {
                flush_phrase(t - low_run * 0.5);
                phrase_start = t;
                energy_sum = e;
                energy_count = 1;
            }
            low_run = 0.0;
        }
    }

    flush_phrase(result.envelope_times.back());
    if (result.phrases.empty()) {
        OfflinePhraseSegment seg;
        seg.start = result.envelope_times.front();
        seg.end = result.envelope_times.back();
        seg.mean_energy = energy_count > 0 ? energy_sum / double(energy_count) : 0.0;
        for (double bt : result.beat_times) {
            if (bt >= seg.start && bt <= seg.end) seg.beat_count++;
        }
        result.phrases.push_back(seg);
    }
}

OfflineAnalysisResult MP3Player::analyze_offline(double envelope_step_sec) {
    OfflineAnalysisResult result;
    if (current_buffer.data.empty() || current_buffer.sample_rate <= 0)
        return result;

    if (is_playing.load())
        stop();

    reset_offline_analysis_state();

    result.file = current_filename;
    result.duration = duration_seconds.load();
    result.envelope_step = std::max(0.02, envelope_step_sec);

    const size_t frame_size = static_cast<size_t>(beat_params.window_size);
    const size_t hop = static_cast<size_t>(beat_params.hop_size);
    const size_t channels = std::max<size_t>(1, current_buffer.channels);
    const size_t total_samples = current_buffer.data.size();

    double last_envelope_t = -1.0;
    double last_energy_spike = -1.0;
    double last_onset = -1.0;
    double last_freq_mod = -1.0;
    double last_harmonic = -1.0;
    double last_dynamic = -1.0;
    bool in_silence = false;
    double silence_start = 0.0;
    double last_beat_t = -1.0;

    for (size_t pos = 0; pos + frame_size <= total_samples; pos += hop) {
        double t = static_cast<double>(pos)
                 / (static_cast<double>(current_buffer.sample_rate) * static_cast<double>(channels));
        position_seconds.store(t);

        const float* samples = current_buffer.data.data() + pos;
        AudioFeatures features = analyze_audio_features(samples, frame_size);

        if (t - last_envelope_t >= result.envelope_step - 1e-6) {
            result.envelope_times.push_back(t);
            result.envelope_energy.push_back(features.energy);
            last_envelope_t = t;
        }

        if (features.beat_detected && t - last_beat_t > 0.12) {
            result.beat_times.push_back(t);
            last_beat_t = t;
        }

        collect_offline_events(features, t, result, last_energy_spike, last_onset,
                               last_freq_mod, last_harmonic, last_dynamic,
                               in_silence, silence_start);
    }

    result.tempo_bpm = calculate_tempo();
    if (result.tempo_bpm <= 0.0 && result.beat_times.size() >= 4) {
        std::vector<double> gaps;
        for (size_t i = 1; i < result.beat_times.size(); ++i)
            gaps.push_back(result.beat_times[i] - result.beat_times[i - 1]);
        std::sort(gaps.begin(), gaps.end());
        double med = gaps[gaps.size() / 2];
        if (med > 0.01)
            result.tempo_bpm = 60.0 / med;
    }

    build_offline_phrases(result);
    return result;
}

} // namespace AudioAnalysis 