/**
 * Offline MP3 analyzer for ShredBlob phrase / timing editor.
 * Usage: shredblob_analyze song.mp3 [-o analysis.json] [--step 0.05]
 */

#include "mp3_player.hpp"
#include <cmath>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>

static const char* eventTypeName(AudioAnalysis::EventType type) {
    using ET = AudioAnalysis::EventType;
    switch (type) {
        case ET::BEAT: return "beat";
        case ET::ENERGY_SPIKE: return "energy_spike";
        case ET::FREQ_MODULATION: return "freq_modulation";
        case ET::HARMONIC_CHANGE: return "harmonic_change";
        case ET::DYNAMIC_CHANGE: return "dynamic_change";
        case ET::TEMPO_CHANGE: return "tempo_change";
        case ET::ONSET: return "onset";
        case ET::SILENCE: return "silence";
        case ET::CONTINUOUS: return "continuous";
        default: return "unknown";
    }
}

static void writeJsonArray(std::ostream& out, const std::vector<double>& values, int precision = 4) {
    out << "[";
    for (size_t i = 0; i < values.size(); ++i) {
        if (i) out << ",";
        out << std::fixed << std::setprecision(precision) << values[i];
    }
    out << "]";
}

static bool writeAnalysisJson(const AudioAnalysis::OfflineAnalysisResult& r, const std::string& path) {
    std::ofstream out(path);
    if (!out) return false;

    out << std::fixed;
    out << "{\n";
    out << "  \"file\": \"" << r.file << "\",\n";
    out << "  \"duration\": " << std::setprecision(3) << r.duration << ",\n";
    out << "  \"tempo_bpm\": " << std::setprecision(2) << r.tempo_bpm << ",\n";
    out << "  \"envelope_step\": " << std::setprecision(3) << r.envelope_step << ",\n";
    out << "  \"envelope_times\": ";
    writeJsonArray(out, r.envelope_times);
    out << ",\n  \"envelope_energy\": ";
    writeJsonArray(out, r.envelope_energy);
    out << ",\n  \"beats\": ";
    writeJsonArray(out, r.beat_times);
    out << ",\n  \"events\": [\n";
    for (size_t i = 0; i < r.events.size(); ++i) {
        const auto& e = r.events[i];
        if (i) out << ",\n";
        out << "    {\"t\": " << std::setprecision(3) << e.time
            << ", \"type\": \"" << eventTypeName(e.type)
            << "\", \"intensity\": " << std::setprecision(4) << e.intensity << "}";
    }
    out << "\n  ],\n  \"phrases\": [\n";
    for (size_t i = 0; i < r.phrases.size(); ++i) {
        const auto& p = r.phrases[i];
        if (i) out << ",\n";
        out << "    {\"start\": " << std::setprecision(3) << p.start
            << ", \"end\": " << p.end
            << ", \"mean_energy\": " << std::setprecision(4) << p.mean_energy
            << ", \"beats\": " << p.beat_count << "}";
    }
    out << "\n  ]\n}\n";
    return true;
}

int main(int argc, char** argv) {
    std::string mp3;
    std::string outPath;
    double step = 0.05;

    for (int i = 1; i < argc; ++i) {
        std::string arg = argv[i];
        if (arg == "-o" && i + 1 < argc) {
            outPath = argv[++i];
        } else if (arg == "--step" && i + 1 < argc) {
            step = std::atof(argv[++i]);
        } else if (arg == "-h" || arg == "--help") {
            std::cout << "Usage: shredblob_analyze song.mp3 [-o analysis.json] [--step 0.05]\n";
            return 0;
        } else if (mp3.empty()) {
            mp3 = arg;
        }
    }

    if (mp3.empty()) {
        std::cerr << "Error: provide an MP3 path.\n";
        return 1;
    }

    AudioAnalysis::MP3Player player;
    AudioAnalysis::AnalysisParams params;
    params.enable_freq_bands = true;
    params.beat_sensitivity = 0.35;
    params.energy_spike_threshold = 0.55;
    player.set_analysis_params(params);

    if (!player.load(mp3)) {
        std::cerr << "Failed to load: " << mp3 << "\n";
        return 1;
    }

    auto result = player.analyze_offline(step);
    std::cerr << "Analyzed " << mp3 << ": "
              << result.duration << "s, tempo ~" << result.tempo_bpm << " BPM, "
              << result.beat_times.size() << " beats, "
              << result.phrases.size() << " phrases, "
              << result.events.size() << " events\n";

    if (outPath.empty()) {
        if (!writeAnalysisJson(result, "/dev/stdout")) {
            std::cerr << "Failed to write JSON to stdout\n";
            return 1;
        }
    } else if (!writeAnalysisJson(result, outPath)) {
        std::cerr << "Failed to write: " << outPath << "\n";
        return 1;
    }

    return 0;
}
