// drag_out — pure implementation. See drag_out.h.

#include "core/ui/drag_out.h"

#include <unordered_set>

namespace reasampler::ui {

namespace {

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

bool needsSurfaceProbe(int px, int py, const PanelClientRect& client) {
    return !insideClient(px, py, client);
}

DropClass decideDropClass(int px, int py, const PanelClientRect& client,
                          const DropContext& ctx) {
    if (!ctx.drag.dragging || !ctx.drag.hasArmedSamples) return DropClass::None;
    if (insideClient(px, py, client)) return DropClass::Internal;

    switch (ctx.surface) {
        case ReaperSurface::OffReaper:
            return DropClass::OsHandoff;

        case ReaperSurface::TrackPanel:
        case ReaperSurface::FxSurface:
            // One instance holds one capture, so a multi payload names no instrument to build —
            // it refuses with a cue rather than falling through to some other surface's outcome.
            return (ctx.singlePayload && ctx.haveTrack) ? DropClass::InstrumentDrop
                                                        : DropClass::Refuse;

        case ReaperSurface::Arrange:
            // GetThingFromPoint may return a null track with a valid info string (the SDK says
            // so): over the arrange that is the region below the last track, which names no lane
            // to place on. Refuse rather than guess a track.
            return ctx.haveTrack ? DropClass::ArrangeInsert : DropClass::Refuse;

        case ReaperSurface::FxEmbed:
        case ReaperSurface::Other:
            return DropClass::Refuse;

        case ReaperSurface::Count:
            break;  // the sentinel is not a surface; it falls to the refusal below
    }
    return DropClass::Refuse;  // an unclassifiable surface still refuses visibly, never silently
}

DropCue cueForDropClass(DropClass cls) {
    switch (cls) {
        case DropClass::Internal:       return DropCue::Internal;
        case DropClass::InstrumentDrop: return DropCue::Instrument;
        case DropClass::ArrangeInsert:  return DropCue::ArrangeInsert;
        case DropClass::Refuse:         return DropCue::Refuse;
        case DropClass::OsHandoff:      return DropCue::OsOwned;
        case DropClass::None:           return DropCue::None;
    }
    return DropCue::None;
}

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()) {
            ++out.skippedUnresolved;
            continue;
        }
        if (!s.fileExists) {
            ++out.skippedMissing;
            continue;
        }
        if (!seen.insert(s.absolutePath).second) {
            ++out.skippedDuplicate;
            continue;
        }
        out.paths.push_back(s.absolutePath);
    }
    return out;
}

OsHandoff decideOsHandoff(const std::vector<std::string>& paths) {
    OsHandoff out;
    out.handOffToOs = !paths.empty();
    return out;
}

} // namespace reasampler::ui
