/*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 "Referee.hpp"
#include "Player/Player.hpp"
#include "Fight/FightScene.hpp"

Referee::Referee(const FightRules &rules) : m_rules(rules),
m_fightState(m_rules.getPlayerCount()), m_receiver(nullptr)
{

}

void Referee::instruct(RefereeListener *receiver)
{
    m_receiver = receiver;
}

void Referee::ready()
{
    //Reset game state to initial ready state
    m_fightState = FightState(m_rules.getPlayerCount());

    if (m_receiver != nullptr)
        m_receiver->readyRound(0);

    m_watch.reset();
}

void Referee::observe(const FightScene &fightScene)
{
    int winnerIx = 0;
    const Player *winner = nullptr;

    if (m_fightState.getPhase() == FightState::Phase::Ready &&
        m_watch.getElapsed() > 1000.f)
    {
        m_fightState.setToFighting();
        if (m_receiver != nullptr)
            m_receiver->startRound();
    }
    else if (m_fightState.getPhase() == FightState::Phase::Fighting &&
             fightScene.isFinished(&winnerIx, &winner))
    {
        if (winnerIx == FightScene::DrawGame || winner == nullptr)
        {
            m_fightState.finishDraw();
            if (m_receiver != nullptr)
                m_receiver->roundOverDraw();
        }
        else
        {
            std::uint8_t accumulatedWins = m_fightState.finishWinner(
                                            static_cast<std::size_t>(winnerIx),
                                            m_rules.getRoundsToWin());
            if (m_receiver != nullptr)
            {
                m_receiver->roundOver(static_cast<std::size_t>(winnerIx),
                                      (*winner), accumulatedWins);
                if (m_fightState.getPhase() == FightState::Phase::Finished)
                    m_receiver->gameOver();
            }
        }

        m_watch.reset();
    }
    else if (m_fightState.getPhase() == FightState::Phase::Break &&
             m_watch.getElapsed() > 4000.f)
    {
        m_fightState.setToReady();
        if (m_receiver != nullptr)
            m_receiver->readyRound(m_fightState.getRoundId());

        m_watch.reset();
    }
}

void Referee::playerWasErased(std::size_t playerIx)
{
    if (m_fightState.playerWasErased(playerIx))
    {
        if (m_rules.getPlayerCount() > 2)
            m_rules.setPlayerCount(m_rules.getPlayerCount() - 1);

        //The player removal lead to an early finish
        if (m_fightState.getPhase() == FightState::Phase::Finished)
            m_receiver->gameOver();
    }
}

void Referee::changeRules(const FightRules &newRules)
{
    if (m_fightState.getPhase() == FightState::Phase::Finished)
        m_rules = newRules;
}

const FightState &Referee::getFightState() const
{
    return m_fightState;
}

const FightRules &Referee::getRules() const
{
    return m_rules;
}
