#include "inventory.hpp"

namespace inventory {
  bool Model::add(const Slot &in_slot, DinkyECS::Entity ent) {
    invariant();

    if(by_slot.contains(in_slot)) return false;

    by_entity.insert_or_assign(ent, in_slot);
    by_slot.insert_or_assign(in_slot, ent);

    return true;
  }

  Slot& Model::get(DinkyECS::Entity ent) {
    return by_entity.at(ent);
  }

  DinkyECS::Entity Model::get(const Slot& slot) {
    return by_slot.at(slot);
  }

  bool Model::has(DinkyECS::Entity ent) {
    return by_entity.contains(ent);
  }

  bool Model::has(const Slot& slot) {
    return by_slot.contains(slot);
  }

  void Model::remove(const Slot& slot, DinkyECS::Entity ent) {
    by_entity.erase(ent);
    by_slot.erase(slot);
    invariant();
  }

  void Model::remove(DinkyECS::Entity ent) {
    auto& slot = by_entity.at(ent);
    remove(slot, ent);
    invariant();
  }

  void Model::remove(const Slot& slot) {
    auto ent = by_slot.at(slot);
    remove(slot, ent);
    invariant();
  }

  void Model::invariant() {
    for(auto& [slot, ent] : by_slot) {
      dbc::check(by_entity.at(ent) == slot,
          fmt::format("mismatched slot {} in by_slot doesn't match entity {}", slot, ent));
    }

    for(auto& [ent, slot] : by_entity) {
      dbc::check(by_slot.at(slot) == ent,
          fmt::format("mismatched entity {} in by_entity doesn't match entity {}", ent, slot));
    }

    dbc::check(by_slot.size() == by_entity.size(), "by_slot and by_entity have differing sizes");
  }

  void Model::dump() {
    invariant();
    fmt::println("INVENTORY has {} slots, sizes equal? {}, contents:",
        by_entity.size(), by_entity.size() == by_slot.size());

    for(auto [slot, ent] : by_slot) {
      fmt::println("slot={}, ent={}, both={}, equal={}",
          slot, ent, by_entity.contains(ent), by_entity.at(ent) == slot);
    }
  }
}