// editor_input_waveform.cpp — the WAVEFORM band's input: grabbing an envelope node or a
// start/loop marker, and resolving both drags live against the pure inverse maps
// (envelope_edit, waveform_view). Windows-only.
//
// Overlay contract: see waveform_view.h's WaveformSurface.

#include "shell/instrument/reasampler_editor.h"

#ifdef _WIN32

#include <algorithm>
#include <cstdint>
#include <vector>

#include "core/instrument/ui/envelope_edit.h"   // nodeAtPoint / resolveNodeDrag
#include "core/instrument/ui/waveform_view.h"   // waveformOverlayArea / markerAtPoint / snap
#include "shell/instrument/editor_internal.h"
#include "shell/instrument/reasampler_processor.h"

namespace reasampler::vst {

using namespace reasampler::ui;
using namespace reasampler::instrument::ui;

bool ReaSamplerEditor::mouseDownWaveform(const FaceLayout& fl, int x, int y) {
    const std::vector<AudioSample>& pcm = monoPcmFor(selectedId_);
    const std::int64_t frames = static_cast<std::int64_t>(pcm.size());
    if (frames <= 0) return false;
    const OverlayArea overlay = waveformOverlayArea(fl.bands.waveform);

    // Envelope nodes first (they sit on top of the markers), then the wave markers.
    const double rate = liveSampleRate();
    if (rate > 0.0) {
        const std::int64_t startFrame = params_.startPoint.value_or(0);
        const AmpEnvelope env = packEnvelope(params_.play, frames, startFrame);
        const double totalSeconds = static_cast<double>(frames) / rate;
        const NodeHit nh = nodeAtPoint(env, overlay, totalSeconds, x, y);
        if (nh.hit) {
            drag_ = DragKind::kEnvNode;
            envNode_ = nh.node;
            dragStartX_ = x;
            dragStartY_ = y;
            dragStartEnv_ = env;
            dragSampleFrames_ = frames;
            dragStartFrame_ = startFrame;
            dragStartParams_ = params_;
            return true;  // node moves once the cursor drags
        }
    }
    const SetupMarkers m = pickedMarkers(frames);
    const std::int64_t markerFrames[3] = {m.start, m.loopStart, m.loopEnd};
    const int hit = markerAtPoint(overlay, frames, markerFrames, 3, x, y);
    if (hit >= 0) {
        drag_ = DragKind::kWaveMarker;
        waveMarker_ = static_cast<WaveMarker>(hit);
        dragStartX_ = x;
        dragStartMarkers_ = m;
        dragSampleFrames_ = frames;
        dragStartParams_ = params_;
        return true;
    }
    return false;
}

void ReaSamplerEditor::dragWaveform(const FaceLayout& fl, int x, int y) {
    const OverlayArea overlay = waveformOverlayArea(fl.bands.waveform);
    const int dx = x - dragStartX_;

    if (drag_ == DragKind::kEnvNode) {
        // Resolve the grabbed envelope node's new params from the pixel delta (through the
        // pure envelope_edit inverse map, clamped + monotonic), then unpack them back onto
        // the parameter set. The AmpEnvelope was snapshotted at grab (dragStartEnv_) so the
        // delta is absolute.
        const std::int64_t frames = dragSampleFrames_;
        const double rate = liveSampleRate();
        if (frames <= 0 || rate <= 0.0) return;
        const double totalSeconds = static_cast<double>(frames) / rate;
        const AmpEnvelope edited = resolveNodeDrag(dragStartEnv_, envNode_, overlay, totalSeconds,
                                                   envClampBounds(), dx, y - dragStartY_);
        unpackEnvelope(edited, frames, dragStartFrame_, params_.play);
        invalidate();  // live feedback; commit on WM_LBUTTONUP
        return;
    }

    // kWaveMarker: resolve the grabbed marker's new frame from the pixel delta,
    // zero-crossing-snap it against the decoded PCM, apply the inter-marker clamps, and write
    // the override live.
    const std::int64_t frames = dragSampleFrames_;
    if (frames <= 0) return;

    // Grabbed frame at grab time, from the snapshot (so the delta is measured from grab).
    const int idx = static_cast<int>(waveMarker_);
    const std::int64_t startVals[3] = {dragStartMarkers_.start, dragStartMarkers_.loopStart,
                                       dragStartMarkers_.loopEnd};
    std::int64_t newFrame = resolveDragFrame(overlay, frames, startVals[idx], dx);

    // Snap to the nearest zero crossing in the decoded PCM. Pure over the cached mono
    // frames — no host types, no file I/O.
    const std::vector<AudioSample>& pcm = monoPcmFor(selectedId_);
    if (!pcm.empty()) {
        newFrame = nearestZeroCrossing(pcm.data(), static_cast<std::int64_t>(pcm.size()),
                                       newFrame);
    }

    // Build the edited marker set from the snapshot, moving only the grabbed marker, then
    // clamp: loopStart <= loopEnd, start in [0, frames-1]. Dragging a loop marker MAKES a loop.
    SetupMarkers m = dragStartMarkers_;
    if (waveMarker_ == WaveMarker::kStart) {
        m.start = newFrame;
    } else if (waveMarker_ == WaveMarker::kLoopStart) {
        m.loopStart = (std::min)(newFrame, m.loopEnd);
        m.hasLoop = true;
    } else {  // kLoopEnd
        m.loopEnd = (std::max)(newFrame, m.loopStart);
        m.hasLoop = true;
    }
    if (m.start < 0) m.start = 0;
    if (m.start > frames - 1) m.start = frames - 1;

    applyMarkers(m);
    invalidate();  // live feedback; the commit lands on release
}

}  // namespace reasampler::vst

#endif  // _WIN32
