#pragma once

#include <cstdint>
#include <cstring>
#include <fstream>
#include <ios>
#include <stdexcept>
#include <string>

/*  1.检查文件是不是标准RIFF/WAVE；
    2.找到fmt块，解析采样率、通道数、位宽等信息；
    3.找到data块；
    4.将文件读取指针移动到第一个IQ 数据字节；
    5.保证后续可以直接用int16_t数组读取I0,Q0,I1,Q1...数据。
*/

struct IQ_file_parameters
{
    double fs = 0.0;                    // 采样率，单位 Hz
    long long filesize = 0;             // 文件总大小，单位字节
    long long headsize = 0;             // IQ采样数据起始位置的字节偏移量
    long long datasize = 0;             // WAV文件data数据块的大小，单位字节

    std::uint16_t audio_format = 0;     // 音频格式：1表示PCM整数，3表示IEEE浮点数
    std::uint16_t channels = 0;         // 通道数，交错IQ数据通常应为2通道
    std::uint32_t sample_rate = 0;      // 整数形式的采样率，与fs表示相同含义
    std::uint32_t byte_rate = 0;        // 每秒数据字节数
    std::uint16_t block_align = 0;      // 一个完整采样帧占用的字节数
    std::uint16_t bits_per_sample = 0;  // 每通道采样位数；使用short/int16_t读取时应为16位
};

namespace FileReadingDetail
{
    // 从文件中读取一个16位小端无符号整数。
    inline std::uint16_t read_u16_le(std::ifstream& fin)
    {
        unsigned char b[2] = {0, 0};
        fin.read(reinterpret_cast<char*>(b), 2);
        if (!fin) throw std::runtime_error("Failed to read uint16 from WAV file.");
        return static_cast<std::uint16_t>(b[0] | (static_cast<std::uint16_t>(b[1]) << 8));
    }

    // 从文件中读取一个32位小端无符号整数。
    inline std::uint32_t read_u32_le(std::ifstream& fin)
    {
        unsigned char b[4] = {0, 0, 0, 0};
        fin.read(reinterpret_cast<char*>(b), 4);
        if (!fin) throw std::runtime_error("Failed to read uint32 from WAV file.");
        return static_cast<std::uint32_t>(b[0]) |
               (static_cast<std::uint32_t>(b[1]) << 8) |
               (static_cast<std::uint32_t>(b[2]) << 16) |
               (static_cast<std::uint32_t>(b[3]) << 24);
    }

    // 读取一个4字节的块标识符，例如RIFF、WAVE、fmt 或data。
    inline void read_fourcc(std::ifstream& fin, char out[5])
    {
        out[0] = out[1] = out[2] = out[3] = 0;
        out[4] = 0;
        fin.read(out, 4);
        if (!fin) throw std::runtime_error("Failed to read WAV chunk id.");
    }
    // 比较当前4字节块标识符是否与目标标识符相同。
    inline bool fourcc_equals(const char id[5], const char* expected)
    {
        return std::memcmp(id, expected, 4) == 0;
    }
    // 获取文件总大小，并在完成后恢复原来的文件读取位置。
    inline long long get_file_size(std::ifstream& fin)
    {
        std::streampos cur = fin.tellg();
        fin.clear();
        fin.seekg(0, std::ios::end);
        std::streampos end = fin.tellg();
        fin.clear();
        fin.seekg(cur, std::ios::beg);
        if (end < 0) return 0;
        return static_cast<long long>(end);
    }
    // 跳过当前WAV子块的数据内容。
    inline void skip_chunk_payload(std::ifstream& fin, std::uint32_t chunk_size)
    {
        // RIFF/WAV子块按偶数字节对齐；如果块长度为奇数，末尾还会有1个填充字节。
        std::uint32_t skip = chunk_size + (chunk_size & 1U);
        fin.seekg(skip, std::ios::cur);
        if (!fin) throw std::runtime_error("Failed while skipping WAV chunk.");
    }
}
// 解析IQ WAV文件头，并将文件读取位置移动到data数据块的起始位置。
inline void parse_head(IQ_file_parameters& head, std::ifstream& fin, const char* data_filepath)
{
    // 检查输入文件是否已经成功打开。
    if (!fin.is_open())
    {
        throw std::runtime_error(std::string("Cannot open IQ WAV file: ") + (data_filepath ? data_filepath : "<null path>"));
    }
    // 清除文件流状态，并从文件开头重新开始解析。
    fin.clear();
    fin.seekg(0, std::ios::beg);
    // 清空旧参数，并记录文件实际总大小。
    head = IQ_file_parameters{};
    head.filesize = FileReadingDetail::get_file_size(fin);
    // 读取并检查RIFF/WAVE文件标识。
    char riff_id[5];
    char wave_id[5];
    FileReadingDetail::read_fourcc(fin, riff_id);
    (void)FileReadingDetail::read_u32_le(fin); // RIFF块长度；对于较大文件，该字段不一定完全可靠
    FileReadingDetail::read_fourcc(fin, wave_id);

    if (!FileReadingDetail::fourcc_equals(riff_id, "RIFF") ||
        !FileReadingDetail::fourcc_equals(wave_id, "WAVE"))
    {
        throw std::runtime_error(std::string("File is not a standard RIFF/WAVE file: ") +
                                 (data_filepath ? data_filepath : "<null path>"));
    }

    bool found_fmt = false;
    bool found_data = false;

    // 逐个遍历WAV子块，直到找到data数据块。
    while (fin && !found_data)
    {
        char chunk_id[5];
        FileReadingDetail::read_fourcc(fin, chunk_id);
        std::uint32_t chunk_size = FileReadingDetail::read_u32_le(fin);

        if (FileReadingDetail::fourcc_equals(chunk_id, "fmt "))
        {
            // 解析fmt格式块。
            found_fmt = true;
            std::streampos fmt_payload_start = fin.tellg();

            head.audio_format = FileReadingDetail::read_u16_le(fin);
            head.channels = FileReadingDetail::read_u16_le(fin);
            head.sample_rate = FileReadingDetail::read_u32_le(fin);
            head.byte_rate = FileReadingDetail::read_u32_le(fin);
            head.block_align = FileReadingDetail::read_u16_le(fin);
            head.bits_per_sample = FileReadingDetail::read_u16_le(fin);
            head.fs = static_cast<double>(head.sample_rate);

            // 某些WAV文件的fmt块包含扩展字段，因此需要准确跳到该块末尾。
            std::streamoff payload_and_padding = static_cast<std::streamoff>(chunk_size + (chunk_size & 1U));
            fin.seekg(fmt_payload_start + payload_and_padding, std::ios::beg);
            if (!fin) throw std::runtime_error("Failed while parsing WAV fmt chunk.");
        }
        else if (FileReadingDetail::fourcc_equals(chunk_id, "data"))
        {
            // 找到data块后，记录数据大小和第一个IQ数据字节的位置。
            found_data = true;
            head.datasize = static_cast<long long>(chunk_size);
            head.headsize = static_cast<long long>(fin.tellg()); // 当前读取位置就是data块第一个数据字节
        }
        else
        {
            // 对于prof、trig、LIST、JUNK等其他子块，根据块长度直接跳过。
            FileReadingDetail::skip_chunk_payload(fin, chunk_size);
        }
    }
    // 必须成功找到fmt格式块。
    if (!found_fmt)
    {
        throw std::runtime_error(std::string("WAV fmt chunk was not found: ") + (data_filepath ? data_filepath : "<null path>"));
    }
    // 必须成功找到data数据块。
    if (!found_data)
    {
        throw std::runtime_error(std::string("WAV data chunk was not found: ") + (data_filepath ? data_filepath : "<null path>"));
    }

    // 当前回放代码按照sizeof(short) * data_amt * 2读取，
    // 因此要求文件为双通道、16位、交错排列的PCM IQ数据。
    if (head.channels != 2)
    {
        throw std::runtime_error("This code expects a 2-channel interleaved I/Q WAV file.");
    }
    // 使用short/int16_t数组读取数据时，每个通道必须是16位。
    if (head.bits_per_sample != 16)
    {
        throw std::runtime_error("This code expects 16-bit PCM samples because data is read into short/int16_t.");
    }
    // audio_format等于1表示整数PCM格式；浮点WAV需要使用float读取或进行类型转换。
    if (head.audio_format != 1)
    {
        throw std::runtime_error("This code expects PCM integer WAV format. Floating-point WAV needs float reading/conversion.");
    }

    // 保证函数返回后，文件读取位置准确位于data数据块的起始位置，
    // 调用者可以直接执行fin.read(...)读取交错IQ数据。
    fin.clear();
    fin.seekg(head.headsize, std::ios::beg);
}
