#pragma once
// Per-mode solo surface: the cache of raw I_SOLO values a real mode switch banks on
// the way out and replays on the way back, plus the two decisions over it. Pure —
// the GetMediaTrackInfo_Value/SetMediaTrackInfo_Value pair is shell/view/view_solo.
// Values are the RAW I_SOLO int, never collapsed to a bool: the SDK's domain is
// 0=off, 1=solo, 2=solo-in-place, 5=safe solo, 6=safe solo-in-place, and all four
// non-zero variants must survive the round trip.

#include <cstddef>
#include <map>
#include <set>
#include <string>
#include <vector>

namespace reasampler::view {

// One live (track GUID, I_SOLO) reading from the shell's enumeration.
struct TrackSolo {
    std::string guid;
    int         solo = 0;
};

// One I_SOLO write the shell must apply.
struct SoloOp {
    std::string guid;
    int         value = 0;

    bool operator==(const SoloOp& o) const { return guid == o.guid && value == o.value; }
};

// The soloed subset of a live enumeration. Zero is the resting value — a track at
// zero is neither cached (nothing to replay) nor cleared (nothing to undo), so a
// project with no solo anywhere produces no cache entry and no project write at
// all. Empty GUIDs are dropped: they can never resolve back to a track.
std::map<std::string, int> soloedTracks(const std::vector<TrackSolo>& live);

// mode id -> (track GUID -> raw I_SOLO). Lifecycle mirrors the park/restore
// snapshots: stored on the way out of a mode, consumed on the way back in, pruned
// when a GUID stops existing.
class SoloCache {
public:
    // Replaces `modeId`'s entry. An EMPTY set removes it rather than storing an
    // empty record — otherwise a serialized cache would parse back to a model that
    // differs from its source, breaking the model's round-trip contract.
    bool store(const std::string& modeId, const std::map<std::string, int>& soloed);

    const std::map<std::string, int>* query(const std::string& modeId) const;

    bool clear(const std::string& modeId);

    const std::map<std::string, std::map<std::string, int>>& all() const { return byMode_; }

    bool empty() const { return byMode_.empty(); }

    // Drops every cached GUID absent from `liveGuids`, and any mode left empty.
    // Returns the number of GUID entries removed.
    //
    // Pruned like the snapshots and unlike membership: a cached solo is a captured
    // prior value awaiting replay onto one specific track, so a stale entry
    // surviving a delete would replay onto whatever track later reuses that GUID —
    // soloing a track the user never soloed and silencing the rest of the mix.
    // The cost is the mirror case: undoing a track delete restores the GUID but not
    // its cached solo. One lost solo the user can see and re-click beats an
    // inexplicable mix-wide mute.
    std::size_t reconcile(const std::set<std::string>& liveGuids);

    bool operator==(const SoloCache& o) const { return byMode_ == o.byMode_; }

private:
    std::map<std::string, std::map<std::string, int>> byMode_;
};

// The incoming mode's restore writes, in GUID order. Two kinds of entry are dropped
// rather than written:
//   * a GUID absent from `liveGuids` — the track is gone (same prune rule as above);
//   * a GUID absent from `visibleGuids` — not visible in the incoming mode, whether
//     because the leaf is parked or because it is a folder parent that is itself
//     derived-invisible there. Either way the track is hidden and (for a parked
//     leaf) carries B_MAINSEND=0, so soloing it would silence the whole mix while
//     contributing nothing audible, and the user would have no visible control to
//     undo it. A show-both leaf is a member of every mode's visible set, so it is
//     never dropped by this rule.
// The caller consumes the whole mode entry regardless (clear-on-restore), so a
// dropped entry does not linger as zombie state waiting on a track that may never
// become visible again.
std::vector<SoloOp> planSoloRestore(const std::map<std::string, int>& cached,
                                    const std::set<std::string>& liveGuids,
                                    const std::set<std::string>& visibleGuids);

} // namespace reasampler::view
