// drag_out — pure implementation. See drag_out.h. NO REAPER / SWELL / OS / vendor.

#include "drag_out.h"

#include <unordered_set>

namespace reasampler {

namespace {

// Half-open point-in-rect (matches the panel's other hit-tests: [x, x+w) x [y, y+h)).
bool insideClient(int px, int py, const PanelClientRect& c) {
    return px >= c.x && px < c.x + c.width &&
           py >= c.y && py < c.y + c.height;
}

} // namespace

DragGesture decideGesture(int px, int py, const PanelClientRect& client,
                          const DragState& state) {
    if (!state.dragging || !state.hasArmedSamples) return DragGesture::None;
    if (insideClient(px, py, client)) return DragGesture::Internal;
    // Outside the client rect (M11 boundary), refined by S17: a SINGLE-capture drag that is
    // still over REAPER's own UI is an instrument drop (heading for a track's FX button);
    // anything else (a multi-capture payload, or the pointer off REAPER entirely) is the
    // unchanged M11 OS drag-out.
    if (state.singleCapture && state.overReaperUi) return DragGesture::InstrumentDrop;
    return DragGesture::OsDrag;
}

PathList assemblePathList(const std::vector<ResolvedSample>& resolved) {
    PathList out;
    std::unordered_set<std::string> seen;
    seen.reserve(resolved.size());

    for (const ResolvedSample& s : resolved) {
        if (s.absolutePath.empty()) {          // shell could not resolve it
            ++out.skippedUnresolved;
            continue;
        }
        if (!s.fileExists) {                   // stale index entry, file gone
            ++out.skippedMissing;
            continue;
        }
        if (!seen.insert(s.absolutePath).second) {  // already emitted this path
            ++out.skippedDuplicate;
            continue;
        }
        out.paths.push_back(s.absolutePath);
    }
    return out;
}

} // namespace reasampler
