/**
 *  Command for spawning new entities into the world.
 *      Copyright 2022 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 ACommandSpawn extends Command;

//  TODO: use spawned name for errors output?
var private ACommandSpawn_Announcer announcer;

protected function Finalizer()
{
    _.memory.Free(announcer);
    super.Finalizer();
}

protected function BuildData(CommandDataBuilder builder)
{
    builder.Group(P("debug"));
    builder.Summary(P("Spawns new entity on the map."));
    builder.ParamText(P("template"),, P("entity"));
    builder.Describe(P("Spawns new entity based on the given template at the point"
            @ "player is currently looking at."));
    builder.SubCommand(P("at"));
    builder.ParamText(P("template"),, P("entity"));
    builder.ParamNumber(P("x"));
    builder.ParamNumber(P("y"));
    builder.ParamNumber(P("z"));
    builder.Describe(P("Spawns new entity based on the given template at"
            @ "the point, given by the coordinates"));
    announcer = ACommandSpawn_Announcer(
        _.memory.Allocate(class'ACommandSpawn_Announcer'));
}

protected function Executed(
    CallData arguments,
    EPlayer instigator,
    CommandPermissions permissions
) {
    local HashTable value;
    local Vector spawnLocation;

    announcer.Setup(none, instigator, othersConsole);
    value = arguments.parameters.GetHashTable(P("template"));
    if (arguments.subCommandName.IsEmpty()) {
        SpawnInInstigatorSight(instigator, value);
    } else if (arguments.subCommandName.Compare(P("at"), SCASE_INSENSITIVE)) {
        spawnLocation.x = arguments.parameters.GetFloat(P("x"));
        spawnLocation.y = arguments.parameters.GetFloat(P("y"));
        spawnLocation.z = arguments.parameters.GetFloat(P("z"));
        SpawnAt(instigator, value, spawnLocation);
    }
    _.memory.Free(value);
}

private final function SpawnAt(
    EPlayer instigator,
    HashTable value,
    Vector spawnLocation
) {
    local Text humanReadable, template;
    local EPlaceable result;

    humanReadable = value.GetText(P("alias"));
    template = value.GetText(P("value"));
    result = _server.kf.world.Spawn(template, spawnLocation);
    if (result != none) {
        announcer.AnnounceSpawned(humanReadable);
    } else {
        announcer.AnnounceSpawningFailed(humanReadable);
    }
    _.memory.Free2(humanReadable, result);
}

private final function SpawnInInstigatorSight(
    EPlayer instigator,
    HashTable value
) {
    local EPlaceable result;
    local Vector spawnLocation;
    local TracingIterator iter;
    local Text humanReadable, template;

    humanReadable = value.GetText(P("alias"));
    template = value.GetText(P("value"));
    iter = _server.kf.world.TracePlayerSight(instigator);
    iter.LeaveOnlyVisible();
    if (iter.HasFinished()) {
        announcer.AnnounceFailedTrace();
        return;
    }
    spawnLocation = iter.GetHitLocation();
    result = _server.kf.world.Spawn(template, spawnLocation);
    //  Shift position back a little and try again;
    //  this should fix a ton of spawning failures
    if (result == none) {
        spawnLocation = spawnLocation + Normal(iter.GetTracingStart() - spawnLocation) * 100;
        result = _server.kf.world.Spawn(template, spawnLocation);
    }
    if (result != none) {
        announcer.AnnounceSpawned(humanReadable);
    } else {
        announcer.AnnounceSpawningFailed(humanReadable);
    }
    _.memory.Free4(result, iter, humanReadable, result);
}

defaultproperties {
    preferredName = "spawn"
}