using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
/// <summary>
/// GAME 1303 Tier 7 — A shelf space that holds a stack of one product. Look at it holding
/// a matching box and press Pickup to stock it; customers take from it. Store Simulator.
///
/// SETUP
/// 1. Empty GameObject on the shelf + BoxCollider (trigger, roughly the shelf space)
/// + this script, on a layer the player's Pickup ray can hit.
/// 2. Assigned Product: what belongs here. Leave empty for "any product" and the first
/// box stocked claims the slot.
/// 3. Capacity: how many units fit.
/// 4. Item Anchor: an empty child where the products sit. The slot instantiates the
/// product's World Prefab in a small grid (Rows/Columns/Spacing) so a stocked shelf
/// actually looks stocked.
/// 5. Stocking: with the player looking at the slot and carrying a matching Carryable,
/// the Pickup key transfers Units Per Press units. That's the loop from the reference
/// games — repetitive on purpose, satisfying because it's visible.
/// 6. OnStockChanged(current, capacity) -> a small floating label if you want one.
///
/// The price is on the ProductType, not here, so a slot doesn't need its own economy.
/// </summary>
public class ShelfSlot : MonoBehaviour
{
[Header("What goes here")]
public ProductType assignedProduct;
public int capacity = 12;
public int unitsPerPress = 3;
[Header("Display")]
public Transform itemAnchor;
public int columns = 3;
public int rows = 2;
public Vector3 spacing = new Vector3(0.25f, 0.25f, 0.25f);
[Header("Interaction")]
public float lookRange = 3f;
public LayerMask playerLayers;
[Header("Feedback")]
public string stockSfx = "stock";
[Header("Events")]
public UnityEvent<int, int> OnStockChanged; // (current, capacity)
public UnityEvent OnEmptied;
public UnityEvent OnFilled;
public int Stock { get; private set; }
public bool IsEmpty => Stock <= 0;
public bool IsFull => Stock >= capacity;
private readonly List<GameObject> displayed = new List<GameObject>();
private PickupCarry watcher;
void Start()
{
if (itemAnchor == null) itemAnchor = transform;
RefreshDisplay();
OnStockChanged?.Invoke(Stock, capacity);
}
void Update()
{
// Is a player looking at us while carrying the right box?
if (watcher == null)
{
var p = GameObject.FindGameObjectWithTag("Player");
if (p != null) watcher = p.GetComponent<PickupCarry>();
if (watcher == null) return;
}
if (!watcher.IsCarrying) return;
if (Keyboard.current == null || !Keyboard.current.eKey.wasPressedThisFrame) return;
// Confirm they're actually aiming at this slot
var cam = watcher.viewCamera;
if (cam == null) return;
if (!Physics.Raycast(cam.transform.position, cam.transform.forward, out RaycastHit hit, lookRange)) return;
if (hit.collider.gameObject != gameObject && hit.collider.transform.parent != transform) return;
TryStockFrom(watcher.Held);
}
/// <summary>Move units from a carried box onto this shelf.</summary>
public bool TryStockFrom(Carryable box)
{
if (box == null || box.product == null || IsFull) return false;
if (assignedProduct != null && box.product != assignedProduct) return false;
if (assignedProduct == null) assignedProduct = box.product;
int room = capacity - Stock;
int taken = box.TakeUnits(Mathf.Min(unitsPerPress, room));
if (taken <= 0) return false;
Stock += taken;
RefreshDisplay();
if (!string.IsNullOrEmpty(stockSfx)) AudioManager.Instance?.PlaySFX(stockSfx);
OnStockChanged?.Invoke(Stock, capacity);
if (IsFull) OnFilled?.Invoke();
return true;
}
/// <summary>A customer takes one. Returns false if the shelf is bare.</summary>
public bool TakeOne()
{
if (IsEmpty) return false;
Stock--;
RefreshDisplay();
OnStockChanged?.Invoke(Stock, capacity);
if (IsEmpty) OnEmptied?.Invoke();
return true;
}
void RefreshDisplay()
{
foreach (var o in displayed) if (o != null) Destroy(o);
displayed.Clear();
if (assignedProduct == null || assignedProduct.worldPrefab == null) return;
int perLayer = Mathf.Max(1, columns * rows);
int show = Mathf.Min(Stock, perLayer * 3); // don't spawn hundreds of meshes
for (int i = 0; i < show; i++)
{
int layer = i / perLayer;
int inLayer = i % perLayer;
int col = inLayer % columns;
int row = inLayer / columns;
Vector3 local = new Vector3(col * spacing.x, layer * spacing.y, row * spacing.z);
var obj = Instantiate(assignedProduct.worldPrefab, itemAnchor);
obj.transform.localPosition = local;
obj.transform.localRotation = Quaternion.identity;
// Display only — no physics, no grabbing
foreach (var rb in obj.GetComponentsInChildren<Rigidbody>()) rb.isKinematic = true;
foreach (var c in obj.GetComponentsInChildren<Collider>()) c.enabled = false;
displayed.Add(obj);
}
}
}