#pragma once
// wav_codec — the pure owner of the WAV/RIFF byte format: chunk walker, layout
// parse, float32 build, size-field patch, and the WAV-aware content hash — one
// chunk traversal shared by all of them so hashing and decoding cannot desync.
// Handles 32-bit float WAV only (RIFF/WAVE, `fmt ` tag 3 or 0xFFFE-extensible
// w/ float subformat, float32 `data`); anything else parses as invalid.

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "core/audio/peaks.h"  // AudioSample (float)

namespace reasampler::capture {

using audio::AudioSample;

// --- Layout parse ------------------------------------------------------------

// The parsed geometry of a canonical 32-bit-float WAV. `valid` is false when the
// bytes are not a WAV we can safely trim; every other field is meaningful only
// when valid.
struct WavLayout {
    bool valid = false;

    std::uint16_t channelCount = 0;   // from `fmt ` (interleave stride)
    std::uint32_t sampleRate   = 0;

    // The `data` chunk: PCM byte offset + declared length.
    // frameCount = dataByteLength / (channelCount * 4).
    std::size_t dataByteOffset = 0;
    std::size_t dataByteLength = 0;

    // Offsets of the two LE uint32 size fields the truncate patch rewrites.
    std::size_t riffSizeFieldOffset = 4;  // always 4 for a RIFF file
    std::size_t dataSizeFieldOffset = 0;

    std::size_t frameCount() const {
        const std::size_t bytesPerFrame = static_cast<std::size_t>(channelCount) * 4u;
        return bytesPerFrame ? dataByteLength / bytesPerFrame : 0;
    }
};

// Parses a WAV byte buffer's header geometry; {valid=false} for anything not a
// canonical float32 RIFF/WAVE, or a `data` length running past the buffer.
// Does not copy PCM, only locates it. Pure + total (no throw, no UB).
WavLayout parseWavLayout(const std::vector<std::uint8_t>& bytes);

// Copies `frameCount` interleaved float frames starting at `startFrame` out of
// the WAV's `data` region into a flat [f0c0,f0c1,...] buffer, clamped to frames
// actually present; never reads past `data`. Reads little-endian via memcpy —
// target is x86/ARM-LE only, no big-endian byte-swap.
std::vector<AudioSample> extractFloatFrames(const std::vector<std::uint8_t>& bytes,
                                            const WavLayout& layout,
                                            std::size_t startFrame,
                                            std::size_t frameCount);

// --- Truncate plan + size-field patch ---------------------------------------

// The plan to truncate a parsed WAV to `keptFrames` frames. `valid` is false if
// the layout is invalid or keptFrames exceeds the file's frames (never grow).
struct WavTruncatePlan {
    bool valid = false;

    std::size_t newFileByteLength = 0;   // truncate the file to exactly this length
    std::size_t dataSizeFieldOffset = 0; // where to write newDataSize (LE uint32)
    std::uint32_t newDataSize = 0;       // kept PCM byte length
    std::size_t riffSizeFieldOffset = 4; // where to write newRiffSize (LE uint32)
    std::uint32_t newRiffSize = 0;       // newFileByteLength - 8 (RIFF size excludes
                                         // the 8-byte "RIFF"+size prefix)
};

// Computes the truncate plan to keep exactly `keptFrames` frames. The shell
// applies it: patch the two size fields (patchU32LE), then truncate to
// newFileByteLength.
WavTruncatePlan planWavTruncate(const WavLayout& layout, std::size_t keptFrames);

// Patches a little-endian uint32 into a byte buffer at `off`. Caller guarantees
// off + 4 <= bytes.size() (the plan's offsets came from a valid parse of the same
// buffer).
void patchU32LE(std::vector<std::uint8_t>& bytes, std::size_t off, std::uint32_t v);

// --- Float32 WAV build -------------------------------------------------------

// Builds a minimal canonical float32 RIFF/WAVE byte buffer from interleaved
// double samples (narrowed to float by cast). Round-trips through
// parseWavLayout/extractFloatFrames.
std::vector<std::uint8_t> buildFloat32Wav(int nch, std::uint32_t rate,
                                          std::size_t frameCount,
                                          const std::vector<double>& interleaved);

// --- Content identity (dedup hashes) -----------------------------------------

// Deterministic FNV-1a 64-bit content hash over `len` bytes, as 16-char lowercase
// hex. Fills Sample::contentHash for the confirm-on-last-reference dedup guardrail.
std::string hashBytes(const std::uint8_t* data, std::size_t len);

// WAV-aware content hash: hashes only the `fmt ` body + `data` payload, skipping
// other chunks. WHY: REAPER's offline renderer embeds a render-varying `bext`
// timestamp chunk even with no BWF metadata requested, so two renders of
// identical audio would otherwise hash differently and never dedup. Prefixed
// with tag byte 'W' so it can't collide with a same-size hashBytes result.
// Falls back to whole-file hashBytes (no prefix) for a file that doesn't parse.
std::string hashWavContent(const std::vector<std::uint8_t>& bytes);

} // namespace reasampler::capture
