//
//  ChargeCommandFactory.cpp
//  eagle0_engine
//
//  Created by Dan Crosby on 1/3/19.
//  Copyright © 2019 none. All rights reserved.
//

#include "ChargeCommandFactory.hpp"

#include "src/main/cpp/net/eagle0/shardok/library/commands/ChargeCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/commands/UnitRestCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/commands/UnitStopCommand.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/HexMapUtils.hpp"
#include "src/main/cpp/net/eagle0/shardok/library/util/ZoneOfControlCalculator.hpp"

namespace shardok {

ChargeCommandFactory::ChargeCommandFactory(const SettingsGetter &getter) : settings(getter) {}

auto ChargeCommandFactory::MakeChargeCommand(
        const Unit *attacker,
        const Unit *defender,
        const Terrain *defenderTerrain,
        const AttackOrientation attackOrientation) const -> CommandSPtr {
    const BattalionTypeSPtr &type = settings.GetBattalionType(attacker->battalion().type());

    const ActionPoints costPoints = type->GetCostToEnterTerrain(defenderTerrain).points;
    ActionCost cost = type->allowsMoveAfterCharge ? ActionCost::StandardActionCost(costPoints)
                                                  : ActionCost::UsesAllActionCost(costPoints);

    return std::make_shared<ChargeCommand>(
            cost,
            settings,
            attacker->player_id(),
            attacker->unit_id(),
            defender->unit_id(),
            defender->location(),
            attackOrientation);
}

void ChargeCommandFactory::AddAvailableChargeCommands(
        CommandList &commands,
        const Unit *unit,
        const ActionPoints remainingActionPoints,
        const Coords &position,
        const bool isEligibleCharger,
        const PossibleChargees &possibleChargees,
        const HexMap *hexMap,
        const Units *units,
        const vector<PlayerId> &allyPids) const {
    if (!isEligibleCharger) return;
    bool foundOne = false;
    const auto &chargerBattalionType = settings.GetBattalionType(unit->battalion().type());
    for (const UnitId chargeTarget : possibleChargees) {
        if (chargeTarget == -1) continue;
        const Unit *defender = units->Get(chargeTarget);
        const Coords &targetCoords = defender->location();
        const Coords &unitLocation = unit->location();
        const Terrain *defenderTerrain = GetTerrain(hexMap, targetCoords);

        if (chargerBattalionType->GetCostToEnterTerrain(defenderTerrain)
                    .IsPossible(remainingActionPoints)) {
            const AttackOrientation orientation = AttackOrientationForAttack(
                    settings,
                    unit->player_id(),
                    units,
                    hexMap,
                    unitLocation,
                    targetCoords);

            commands.push_back(MakeChargeCommand(unit, defender, defenderTerrain, orientation));
            foundOne = true;
        }
    }
    if (foundOne) {
        if (IsInEnemyZoc(settings, unit->player_id(), units, hexMap, allyPids, position)) {
            commands.push_back(std::make_shared<UnitRestCommand>(
                    unit->player_id(),
                    unit->unit_id(),
                    settings.Backing().action_cost_to_vigor_cost(),
                    settings.GetBattalionType(unit->battalion().type())->adjustsMorale));
        } else {
            commands.push_back(
                    std::make_shared<UnitStopCommand>(unit->player_id(), unit->unit_id()));
        }
    }
}
void ChargeCommandFactory::AddAvailableCommands(CommandList &commands, const CommandParams &params)
        const {
    AddAvailableChargeCommands(
            commands,
            params.unit,
            params.remainingActionPoints,
            params.position,
            params.isEligibleCharger,
            params.possibleChargees,
            params.hexMap,
            params.units,
            params.allyPids);
}

}  // namespace shardok
