/**
 * Author: dkanus
 * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
 * License: GPL
 * Copyright 2023 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 CommandsTool extends CmdItemsTool;

//! This is a base class for auxiliary objects that will be used for storing
//! named [`Command`] instances.
//!
//! This storage class allows for efficient manipulation and retrieval of
//! [`Command`]s, along with information about what use groups were authorized
//! to use them.
//!
//! Additionally, this tool allows for efficient fetching of commands that
//! belong to a particular *command group*.

/// [`HashTable`] that maps a command group name to a set of command names that
/// belong to it.
var private HashTable groupedCommands;

protected function Constructor() {
    super.Constructor();
    groupedCommands = _.collections.EmptyHashTable();
}

protected function Finalizer() {
    super.Finalizer();
    _.memory.Free(groupedCommands);
    groupedCommands = none;
}

/// Returns all known command groups' names.
public final function array<Text> GetGroupsNames() {
    local array<Text> emptyResult;

    if (groupedCommands != none) {
        return groupedCommands.GetTextKeys();
    }
    return emptyResult;
}

/// Returns array of names of all available commands belonging to the specified
/// group.
public final function array<Text> GetCommandNamesInGroup(BaseText groupName) {
    local int i;
    local ArrayList commandNamesArray;
    local array<Text> result;

    if (groupedCommands == none)    return result;
    commandNamesArray = groupedCommands.GetArrayList(groupName);
    if (commandNamesArray == none)  return result;

    for (i = 0; i < commandNamesArray.GetLength(); i += 1) {
        result[result.length] = commandNamesArray.GetText(i);
    }
    _.memory.Free(commandNamesArray);
    return result;
}

protected function ItemCard MakeCard(class<AcediaObject> commandClass, BaseText itemName) {
    local Command newCommandInstance;
    local ItemCard newCard;
    local Text commandGroup;

    if (class<Command>(commandClass) != none) {
        newCommandInstance = Command(_.memory.Allocate(commandClass, true));
        newCommandInstance.Initialize(itemName);
        newCard = ItemCard(_.memory.Allocate(class'ItemCard'));
        newCard.InitializeWithInstance(newCommandInstance);

        // Guaranteed to be lower case (keys of [`HashTable`])
        if (itemName != none) {
            itemName = itemName.LowerCopy();
        } else {
            itemName = newCommandInstance.GetPreferredName();
        }
        commandGroup = newCommandInstance.GetGroupName();
        AssociateGroupAndName(commandGroup, itemName);
        _.memory.Free3(newCommandInstance, itemName, commandGroup);
    }
    return newCard;
}

protected function DiscardCard(ItemCard toDiscard) {
    local Text groupKey, commandName;
    local Command storedCommand;
    local ArrayList listOfCommands;

    if (toDiscard == none)      return;
    // Guaranteed to store a [`Command`]
    storedCommand = Command(toDiscard.GetItem());
    if (storedCommand == none)  return;

    // Guaranteed to be stored in a lower case
    commandName = storedCommand.GetName();
    listOfCommands = groupedCommands.GetArrayList(groupKey);
    if (listOfCommands != none && commandName != none) {
        listOfCommands.RemoveItem(commandName);
    }
    _.memory.Free2(commandName, storedCommand);
}

// Expect both arguments to be not `none`.
// Expect both arguments to be lower-case.
private final function AssociateGroupAndName(BaseText groupKey, BaseText commandName) {
    local ArrayList listOfCommands;

    if (groupedCommands != none) {
        listOfCommands = groupedCommands.GetArrayList(groupKey);
        if (listOfCommands == none) {
            listOfCommands = _.collections.EmptyArrayList();
        }
        if (listOfCommands.Find(commandName) < 0) {
            // `< 0` means not found
            listOfCommands.AddItem(commandName);
        }
        // Set `listOfCommands` in case we've just created that array.
        // Won't do anything if it is already recorded there.
        groupedCommands.SetItem(groupKey, listOfCommands);
    }
}

defaultproperties {
    ruleBaseClass = class'Command';
}