/*Copyright (C) 2010-2023 Duncan Deveaux

This file is part of Hikou no mizu.

Hikou no mizu 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, either version 3 of the License, or
(at your option) any later version.

Hikou no mizu 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 Hikou no mizu.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ServerFightController.hpp"
#include "Fight/Referee.hpp"
#include "Network/Serialization/PlayerAction.hpp"
#include "Network/Serialization/DamageStatus.hpp"
#include "Network/Serialization/EtherStatus.hpp"

#include "Player/Player.hpp"
#include "Tools/Log.hpp"
#include "Tools/Format.hpp"
#include <cassert>

#ifdef HNMSERVER_DRAW
    #include "Graphics/Camera.hpp"
#endif

ServerFightController::ServerFightController(FightRules::Mode mode,
                                             const std::string &arena)
#ifdef HNMSERVER_DRAW
: m_fightScene(m_textureManager)
#endif
{
    setArena(arena);
    m_fightScene.addFightModeController(mode);
    m_fightScene.start();
}

void ServerFightController::setArena(const std::string &arenaName)
{
    Log::info(Format::format("Arena> Loaded arena: {}", arenaName));
    m_fightScene.setArena(arenaName);
}

void ServerFightController::addPlayer(const PeerData &player)
{
    ServerPlayerInterface *playerControl =
            m_fightScene.addNetServerPlayer(player.character);

    if (playerControl != nullptr)
        m_playingPeers.push_back(PlayingPeerData(player, playerControl));

    assert(m_playingPeers.size() == m_fightScene.getPlayers().size());
}

void ServerFightController::removePlayer(std::size_t playerIx)
{
    assert(m_playingPeers.size() == m_fightScene.getPlayers().size());

    if (playerIx < m_playingPeers.size() && m_fightScene.removePlayer(playerIx))
    {
        m_playingPeers.erase(std::next(m_playingPeers.begin(),
            static_cast<std::vector<PlayingPeerData>::difference_type>(playerIx)));
    }
}

void ServerFightController::update(float frameTime)
{
    m_fightScene.update(frameTime);
    #ifdef HNMSERVER_DRAW
        Camera(Box(0.f, 0.f, 2880.f, 1620.f)).look(1024, 576);
        m_fightScene.draw();
    #endif
}

void ServerFightController::applyAction(std::size_t playerIx,
                                        const PlayerAction &action)
{
    if (playerIx < m_playingPeers.size())
        m_playingPeers[playerIx].player->playerActionReceived(action);
}

void ServerFightController::showReferee(Referee &referee) const
{
    referee.observe(m_fightScene);
}

void ServerFightController::readyRound()
{
    //Reset fight scene
    m_fightScene.reset();

    //Reset player controller to cancel any ongoing crouching/moves
    for (PlayingPeerData &playingPeer : m_playingPeers)
        playingPeer.player->reset();
}

void ServerFightController::startRound()
{
    m_fightScene.start();
}

void ServerFightController::revivePlayers()
{
    m_fightScene.revivePlayers();
}

std::vector<PlayerStatus> ServerFightController::getPlayerStatus() const
{
    //Gather the current player status
    std::vector<PlayerStatus> playerStatus;
    playerStatus.reserve(m_playingPeers.size());

    for (const PlayingPeerData &peer : m_playingPeers)
        playerStatus.push_back(peer.player->getPlayerStatus());

    return playerStatus;
}

std::vector<PhysicsObjectStatus> ServerFightController::getPlatformStatus() const
{
    return m_fightScene.getPlatformStatus();
}

std::vector<DamageStatus> ServerFightController::getDamageStatus(
    const Timer::TimePoint &serverTime) const
{
    return DamageStatus::readDamageStatusList(serverTime,
                                              m_fightScene.getQueuedDamages(),
                                              m_fightScene.getPlayers());
}

EtherStatus ServerFightController::getWeaponStatus() const
{
    return m_fightScene.getWeaponEtherStatus();
}
