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

var private array<SideEffect> activeSideEffects;

/// Returns an array containing all SideEffect objects that have been registered up to this point. 
///
/// The order of the elements in the array is not guaranteed.
public function array<SideEffect> GetAll() {
    local int i;

    for (i = 0; i < activeSideEffects.length; i += 1) {
        activeSideEffects[i].NewRef();
    }
    return activeSideEffects;
}

/// Returns all registered [`SideEffects`] that are associated with the specified package name
/// (case-insensitive).
public function array<SideEffect> GetFromPackage(BaseText packageName) {
    local int i;
    local Text nextPackage;
    local array<SideEffect> result;

    if (packageName == none) {
        return result;
    }
    for (i = 0; i < activeSideEffects.length; i += 1) {
        nextPackage = activeSideEffects[i].GetPackage();
        if (packageName.Compare(nextPackage, SCASE_INSENSITIVE)) {
            activeSideEffects[i].NewRef();
            result[result.length] = activeSideEffects[i];
        }
        _.memory.Free(nextPackage);
    }
    return result;
}

/// Adds a new side effect to the list of active side effects.
///
/// This method will fail if any of its arguments are `none` or a side effect with that exact
/// contents was already added.
public function SideEffect Add(
    BaseText sideEffectName,
    BaseText sideEffectDescription,
    BaseText sideEffectPackage,
    BaseText sideEffectSource,
    BaseText sideEffectStatus
) {
    local bool initialized;
    local SideEffect newSideEffect;

    newSideEffect = SideEffect(_.memory.Allocate(class'SideEffect'));
    initialized = newSideEffect.Initialize(
        sideEffectName,
        sideEffectDescription,
        sideEffectPackage,
        sideEffectSource,
        sideEffectStatus);
    if (initialized) {
        if (!AddInstance(newSideEffect)) {
            _.memory.Free(newSideEffect);
            return none;
        }
    } else {
        _.memory.Free(newSideEffect);
        return none;
    }
    return newSideEffect;
}

/// Adds a new side effect to the list of active side effects.
///
/// This method will fail if a side effect with that exact contents was already added.
public function SideEffect Add_S(
    string sideEffectName,
    string sideEffectDescription,
    string sideEffectPackage,
    string sideEffectSource,
    string sideEffectStatus
) {
    local bool initialized;
    local SideEffect newSideEffect;

    newSideEffect = SideEffect(_.memory.Allocate(class'SideEffect'));
    initialized = newSideEffect.Initialize_S(
        sideEffectName,
        sideEffectDescription,
        sideEffectPackage,
        sideEffectSource,
        sideEffectStatus);
    if (initialized) {
        if (!AddInstance(newSideEffect)) {
            _.memory.Free(newSideEffect);
            return none;
        }
    } else {
        return none;
    }
    return newSideEffect;
}

/// Checks whether specified [`SideEffect`] is currently active.
///
/// Check is done via contents and not instance equality.
/// Returns `true` if specified [`SideEffect`] is currently active and `false` otherwise.
public function bool IsRegistered(SideEffect sideEffectToCheck) {
    local int i;

    if (sideEffectToCheck == none)          return false;
    if (!sideEffectToCheck.IsInitialized()) return false;

    for (i = 0; i < activeSideEffects.length; i += 1) {
        if (activeSideEffects[i].IsEqual(sideEffectToCheck)) {
            return true;
        }
    }
    return false;
}

/// Adds a new side effect to the list of active side effects.
///
/// This method will fail if its argument is `none`, non-initialized or a side effect with that
/// exact contents was already added.
public function bool AddInstance(SideEffect newSideEffect) {
    local int i;

    if (newSideEffect == none)          return false;
    if (!newSideEffect.IsInitialized()) return false;

    for (i = 0; i < activeSideEffects.length; i += 1) {
        if (activeSideEffects[i].IsEqual(newSideEffect)) {
            return false;
        }
    }
    newSideEffect.NewRef();
    activeSideEffects[activeSideEffects.length] = newSideEffect;
    LogAddingSideEffectChange(newSideEffect, true);
    return true;
}

/// Removes a side effect from the list of active side effects.
///
/// This method will fail if its argument is `none`, non-initialized or a side effect with its
/// contents isn't in the records.
public function bool RemoveInstance(SideEffect inactiveSideEffect) {
    local int i;
    local bool foundInstance;

    if (inactiveSideEffect == none) {
        return false;
    }
    for (i = 0; i < activeSideEffects.length; i += 1) {
        if (activeSideEffects[i].IsEqual(inactiveSideEffect)) {
            LogAddingSideEffectChange(activeSideEffects[i], false);
            _.memory.Free(activeSideEffects[i]);
            activeSideEffects.Remove(i, 1);
            foundInstance = true;
        }
    }
    return foundInstance;
}

private function LogAddingSideEffectChange(SideEffect effect, bool added) {
    local MutableText builder;
    local Text sideEffectData;

    if (effect == none) {
        return;
    }
    builder = _.text.Empty();
    if (added) {
        builder.Append(P("NEW SIDE EFFECT: "));
    } else {
        builder.Append(P("REMOVED SIDE EFFECT: "));
    }
    sideEffectData = effect.GetName();
    builder.Append(sideEffectData);
    _.memory.Free(sideEffectData);
    sideEffectData = effect.GetStatus();
    if (sideEffectData != none) {
        builder.Append(P(" {"));
        builder.Append(sideEffectData);
        _.memory.Free(sideEffectData);
        builder.Append(P("}"));
    }
    _.logger.Info(builder);
    builder.FreeSelf();
}

defaultproperties {
}