/**
 * Author: dkanus
 * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
 * License: GPL
 * Copyright 2021-2024 Anton Tarasenko
 *------------------------------------------------------------------------------
 * This file is part of Acedia.
 *
 * Acedia is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License, or
 * (at your option) any later version.
 *
 * Acedia is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Acedia.  If not, see <https://www.gnu.org/licenses/>.
 */
class EInventory extends EInterface
    abstract;

//! Abstract interface that represents inventory system.
//!
//! The inventory system is designed to handle items in a player's inventory.
//! In Killing Floor, it is a simple set of items with total weight limitations.
//! Any other kind of inventory can be implemented as long as it follows
//! the limitations of this interface.

/// Describes mapping between equipped item and slots that it is equipped in.
struct ItemSlotsPair {
    /// Item for which we describe a set of slots.
    var public EItem equippedItem;
    /// Case-insensitive names of the slots in which item is equipped.
    var public array<Text> slotNames;
};

/// Initializes `EInventory` for a given `player`.
///
/// This method should not be called manually, unless you implement your own
/// game interface.
///
/// It cannot fail for any connected player and assumes it will not be called for
/// disconnected players.
public function Initialize(EPlayer player);

/// Adds the given [`EItem`] to the inventory system and returns a reference to the added item.
///
///
/// If adding [`newItem`] is not possible, the inventory system may refuse it.
/// The [`newItem`] may be destroyed as a result of this call.
/// For example, it could be merged with another item in the inventory.
/// In such cases, the method returns the resulting [`EItem`] in the inventory.
/// Example: adding a single pistol when the inventory already contains a pistol
/// of the same type may result in merging them.
///
/// Returns `none` if and only if the inventory failed to add [`newItem`].
///
/// Parameter [`forceAddition`] is relevant when [`newItem`] cannot be added
/// under normal circumstances.
/// Setting this to `true` allows the inventory system to "bend the rules" to
/// add the item.
/// The inventory system may remove conflicting items to make room for
/// [`newItem`].
/// Removal of items is only allowed if it enables the addition of [`newItem`],
/// and the removal process is implementation-specific.
public function EItem Add(EItem newItem, optional bool forceAddition) {
    return none;
}

/// Adds an [`EItem`] described by the template [`newItemTemplate`] to
/// the inventory system and returns a reference to the added item.
///
/// If adding the new item is not possible, the inventory system may refuse it.
/// The newly created item may be destroyed as a result of this call.
/// For example, it could be merged with another item in the inventory.
/// In such cases, the method returns the resulting [`EItem`] in the inventory.
/// Example: adding a single pistol when the inventory already contains a pistol
/// of the same type may result in merging them.
///
/// Returns `none` if and only if the inventory failed to add the new item.
///
/// **Parameters:**
/// Parameter [`forceAddition`] is relevant when the new item cannot be added
/// under normal circumstances.
/// Setting this to `true` allows the inventory system to "bend the rules" to
/// add the item.
/// The inventory system may remove conflicting items to make room for
/// the new item.
/// Removal of items is only allowed if it enables the addition of the new item,
/// and the removal process is implementation-specific.
public function EItem AddItemFromTemplate(BaseText newItemTemplate, optional bool forceAddition) {
    return none;
}

/// Checks if the given [`itemToCheck`] can be added to the inventory.
///
/// This method determines whether the specified [`EItem`] can be added to
/// the caller inventory system, considering the [`forceAddition`] flag:
/// if it is set to `true`, the inventory will attempt to "bend the rules" to
/// add the item.
/// Defaults to `false`.
///
/// Returns `true` if [`itemToCheck`] can be added to the inventory with
/// the given `forceAddition` flag and `false` otherwise.
/// If you'd like more detailed error report, use method
/// [`EInventory::CanAddExplain()`].
public final function bool CanAdd(EItem itemToCheck, optional bool forceAddition) {
    local bool success;
    local AcediaError explanation;

    explanation = CanAddExplain(itemToCheck, forceAddition);
    success = (explanation == none);
    _.memory.Free(explanation);
    return success;
}

/// Checks if an item created from the given template can be added to
/// the inventory.
///
/// This method determines whether an item instantiated from template
/// [`itemTemplateToCheck`] can be added to the caller inventory system,
/// considering the [`forceAddition`] flag:
/// if it is set to `true`, the inventory will attempt to "bend the rules" to
/// add the item.
/// Defaults to `false`.
///
/// Returns `true` if an item from [`itemTemplateToCheck`] can be added to
/// the inventory with the given [`forceAddition`] flag and `false` otherwise.
/// If you'd like more detailed error report, use method
/// [`EInventory::CanAddItemFromTemplateExplain()`].
public function bool CanAddItemFromTemplate(
    BaseText itemTemplateToCheck,
    optional bool forceAddition
) {
    local bool success;
    local AcediaError explanation;

    explanation = CanAddItemFromTemplateExplain(itemTemplateToCheck, forceAddition);
    success = (explanation == none);
    _.memory.Free(explanation);
    return success;
}

/// Checks if the given [`itemToCheck`] can be added to the inventory and
/// provides an explanation if it cannot.
///
/// This method determines whether the specified [`EItem`] can be added to
/// the caller inventory system, considering the [`forceAddition`] flag:
/// if `true`, the inventory will attempt to "bend the rules" to add the item.
/// Defaults to `false`.
///
/// If the item cannot be added, it returns an [`AcediaError`] that contains
/// appropriate details; otherwise, it returns `none`.
///
/// See also [`EInventory::CanAdd()`] method that returns a simple boolean
/// indicating if the item can be added.
public function AcediaError CanAddExplain(EItem itemToCheck, optional bool forceAddition) {
    return none;
}

/// Checks if an item created from the given template can be added to
/// the inventory and provides an explanation if it cannot.
///
/// This method determines whether an item instantiated from template
/// [`itemTemplateToCheck`] can be added to the caller inventory system,
/// considering the [`forceAddition`] flag:
/// if `true`, the inventory will attempt to "bend the rules" to add the item.
/// Defaults to `false`.
///
/// If the item cannot be added, it returns an [`AcediaError`] that contains
/// appropriate details; otherwise, it returns `none`.
///
/// See also [`EInventory::CanAddTemplate()`] method that returns a simple
/// boolean indicating if the item can be added.
public function AcediaError CanAddItemFromTemplateExplain(
    BaseText itemTemplateToCheck,
    optional bool forceAddition
) {
    return none;
}

/// Removes the specified [`itemToRemove`] from the inventory.
///
/// This method attempts to remove the given [`EItem`] from the caller
/// inventory system.
/// The inventory may refuse to remove items where [`EItem::IsRemovable()`]
/// returns `false`, unless [`forceRemoval`] is set to `true`.
///
/// Argument [`keepItem`] determines whether removed item should be
/// destroyed.
/// If `false` (default), the removed item is destroyed.
/// If `true`, the inventory will attempt to preserve the item in some way
/// (e.g., dropping it in the world).
///
/// Method returns `true` if the item was removed and `false` if the item
/// was not removed (including if it was not in the inventory).
public function bool Remove(
    EItem itemToRemove,
    optional bool keepItem,
    optional bool forceRemoval
) {
    return false;
}

/// Removes item(s) of the specified type from the inventory.
///
/// This method attempts to remove item(s) that have [`itemTypeToRemove`] type
/// from the caller inventory system.
/// By default, it removes one arbitrary item, but setting [`removeAll`] to
/// `true` will remove all items of that type.
/// The inventory may refuse to remove items where [`EItem::IsRemovable()`]
/// returns `false`, unless [`forceRemoval`] is set to `true`.
///
/// Argument [`keepItem`] determines whether removed items should be
/// destroyed.
/// If `false` (default), the removed items are destroyed.
/// If `true`, the inventory will attempt to preserve items in some way
/// (e.g., dropping them in the world).
///
/// Returns `true` if any item(s) were removed and `false` if no item(s) were
/// removed (including if no items of the given type were in the inventory).
public function bool RemoveItemOfType(
    BaseText itemTypeToRemove,
    optional bool keepItem,
    optional bool forceRemoval,
    optional bool removeAll
) {
    return false;
}

/// Removes all items from the inventory.
///
/// By default, this method removes only items with the "visible" tag
/// (i.e., items the player can see and interact with).
/// Setting [`includeHidden`] to `true` will also remove items without
/// the "visible" tag.
///
/// The inventory may refuse to remove items where [`EItem::IsRemovable()`]
/// returns `false`, unless `forceRemoval` is set to `true`.
///
/// Argument [`keepItem`] determines whether removed items should be
/// destroyed.
/// If `false` (default), the removed items are destroyed.
/// If `true`, the inventory will attempt to preserve items in some way
/// (e.g., dropping them in the world).
///
/// Returns `true` if any items were removed and `false` if no items were
/// removed (including if the inventory was already empty).
public function bool RemoveAll(
    optional bool keepItems,
    optional bool forceRemoval,
    optional bool includeHidden
) {
    return false;
}

/// Checks if the inventory contains the specified [`itemToCheck`].
///
/// Returns `true` if [`itemToCheck`] is in the inventory and `false` otherwise.
public function bool Contains(EItem itemToCheck) {
    return false;
}

/// Checks if the inventory contains any item of the specified type.
///
/// Returns `true` if an item created from [`typeToCheck`] is in
/// the inventory and `false` otherwise.
public function bool HasItemOfType(BaseText typeToCheck) {
    return false;
}

/// Retrieves all items currently in the inventory.
///
/// The returned array is guaranteed not to contain `none` references or
/// interfaces to nonexistent entities.
public function array<EItem> GetAllItems() {
    local array<EItem> emptyArray;

    return emptyArray;
}

/// Retrieves all items in the inventory that support a specified interface.
///
/// The returned array is guaranteed not to contain `none` references or
/// interfaces to nonexistent entities.
public function array<EItem> GetItemsSupporting(class<EItem> interfaceClass) {
    local array<EItem> emptyArray;

    return emptyArray;
}

/// Retrieves all items in the inventory that have the specified tag.
///
/// The returned array is guaranteed not to contain `none` references or
/// interfaces to nonexistent entities.
public function array<EItem> GetTagItems(BaseText tag) {
    local array<EItem> emptyArray;

    return emptyArray;
}

/// Retrieves one item from the inventory that has the specified tag.
///
/// If multiple items have the specified tag, the inventory system may select
/// one arbitrarily.
/// The selected item may vary between calls.
///
/// Returns `none` if there is no item has specified tag.
/// Otherwise the returned value is guaranteed not to be `none` or refer to
/// a nonexistent entity.
public function EItem GetTagItem(BaseText tag) {
    return none;
}

/// Retrieves all items in the inventory that have a specified type.
///
/// The returned array is guaranteed not to contain `none` references or
/// interfaces to nonexistent entities.
public function array<EItem> GetItemsOfType(BaseText template) {
    local array<EItem> emptyArray;

    return emptyArray;
}

/// Retrieves one item from the inventory that has a specified type.
///
/// If multiple items originated from the specified template, the inventory
/// system may select one arbitrarily. The selected item may vary between calls.
///
/// Returns `none` if there is no item of the given type.
/// Otherwise the returned value is guaranteed not to be `none` or refer to
/// a nonexistent entity.
public function EItem GetItemOfType(BaseText template) {
    return none;
}

/// Returns array with all available slot names.
///
/// Slot is a designated position in the inventory for a specific type of item,
/// such as "weapon," "armor," or "boots".
/// Slot names are case-insensitive.
/// Each slot can hold only one item, though some items can occupy multiple
/// slots simultaneously.
///
/// The returned array is guaranteed to not contain `none`.
public function array<Text> GetSlots() {
    local array<Text> emptyArray;

    return emptyArray;
}

/// Retrieves all items in the inventory that are currently equipped by
/// its owner.
///
/// Returns an array of [`EItem`]s that are equipped by the inventory's owner.
/// The returned array is guaranteed not to contain `none` references or
/// interfaces to nonexistent entities.
public function array<EItem> GetEquippedItems() {
    local array<EItem> emptyArray;

    return emptyArray;
}

/// Retrieves all equipped items in the inventory along with their corresponding
/// slots.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
///
/// Returns an array of [`ItemSlotsPair`] structs, each mapping an equipped item
/// to its slots.
/// This array contains no `none` references or interfaces to nonexistent
/// entities and ensures that each slot is uniquely listed in only one
/// [`ItemSlotsPair`] struct.
public function array<ItemSlotsPair> GetEquippedItemsWithSlots() {
    local array<ItemSlotsPair> emptyArray;

    return emptyArray;
}

/// Returns the "main" equipped item.
///
/// This method primarily summarizes the player's current state by identifying
/// the main equipped item, which varies based on specific gameplay
/// considerations.
/// For instance, in Killing Floor, this would typically be the currently
/// equipped weapon.
///
/// Returns `none` if no main item is equipped.
/// Guaranteed to not return reference to a non-existent entity.
public function EItem GetEquippedItem() {
    return none;
}

/// Returns the item equipped in the specified slot.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
///
/// If you want to find a slot that is occupied by a specific item,
/// see [`EInventory::FindItemSlots()`].
///
/// Returns `none` if no item is equipped in the specified slot.
/// Guaranteed to not return reference to a non-existent entity.
public function EItem GetItemInSlot(Text slotName) {
    return none;
}

/// Returns all slots, occupied by the specified item.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
///
/// If you want to find an item that occupies a specific slot,
/// see [`EInventory::GetItemInSlot()`].
///
/// The returned array is guaranteed to not contain `none`s or references to
/// a non-existent entities.
public function array<Text> FindItemSlots(EItem item) {
    local array<Text> emptyArray;

    return emptyArray;
}

/// Equips given item into a specified slot.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public function AcediaError EquipItem(EItem itemToEquip, Text slotName) {
    return none;
}

/// Checks if given item can be equipped into a specified slot.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public final function bool CanEquipItem(EItem itemToEquip, Text slotName) {
    local bool success;
    local AcediaError explanation;

    explanation = CanEquipItemExplain(itemToEquip, slotName);
    success = (explanation == none);
    _.memory.Free(explanation);
    return success;
}

/// Checks if given item can be equipped into a specified slot and provides
/// an explanation if it cannot.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public function AcediaError CanEquipItemExplain(EItem itemToEquip, Text slotName) {
    return none;
}

/// Unequips given item.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public function AcediaError UnequipItem(EItem itemToUnequip) {
    return none;
}

/// Checks if given item can be unequipped.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public final function bool CanUnequipItem(EItem itemToUnequip) {
    local bool success;
    local AcediaError explanation;

    explanation = CanUnequipItemExplain(itemToUnequip);
    success = (explanation == none);
    _.memory.Free(explanation);
    return success;
}

/// Checks if given item can be unequipped and provides an explanation if
/// it cannot.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public function AcediaError CanUnequipItemExplain(EItem itemToUnequip) {
    return none;
}

/// Unequips an item from  the specified slot.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public function AcediaError UnequipFromSlot(BaseText slotToUnequipFrom) {
    return none;
}

/// Checks if an item can be unequipped from the specified slot.
///
/// Returns false if the slot is empty.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public final function bool CanUnequipFromSlot(EItem slotToUnequipFrom) {
    local bool success;
    local AcediaError explanation;

    explanation = CanUnequipFromSlotExplain(slotToUnequipFrom);
    success = (explanation == none);
    _.memory.Free(explanation);
    return success;
}

/// Checks if an item can be unequipped from the specified slot and provides
/// an explanation if it cannot.
///
/// If a slot is empty, then item cannot be unequipped from it.
///
/// See [`EInventory::GetSlots()`] for the description of what a slot is.
public function AcediaError CanUnequipFromSlotExplain(EItem slotToUnequipFrom) {
    return none;
}

defaultproperties {
}