/*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 "LocalGame.hpp"
#include "Engines/GameEngine.hpp"
#include "Graphics/Window.hpp"
#include "Configuration/Configuration.hpp"
#include "LocalFightController.hpp"

#include "Fight/FightCameraHandler.hpp"
#include "PauseTab.hpp"
#include "EndTab.hpp"
#include "GUI/Fight/RoundAnnouncer.hpp"
#include "GUI/Fight/FightInterface.hpp"
#include "GUI/Fight/RoundResultInfo.hpp"
#include "GUI/Fight/FpsCounter.hpp"
#include "Engines/Resources/TextureManager.hpp"
#include "Engines/Resources/FontManager.hpp"
#include "Engines/Resources/JoystickManager.hpp"
#include "Engines/Resources/SoundBufferManager.hpp"
#include "Engines/Sound/PlaylistPlayer.hpp"
#include "Engines/Sound/AmbientPlayer.hpp"
#include "Engines/Sound/SoundEngine.hpp"
#include "Graphics/Camera.hpp"
#include "Tools/ScaledPixel.hpp"
#include "Tools/Format.hpp"
#include "Tools/Timer.hpp"
#include "Tools/Log.hpp"
#include "Tools/Format.hpp"
#include "Structs/Vector.hpp"
#include "Structs/JoystickData.hpp"

LocalGame::LocalGame(GameEngine &gameEngine) :
m_gameEngine(&gameEngine), m_window(&(gameEngine.getWindow())),
m_configuration(&(gameEngine.getConfiguration()))
{

}

void LocalGame::setArena(const std::string &arenaName)
{
    m_arena = arenaName;
}

void LocalGame::setRules(const FightRules &rules)
{
    m_rules = rules;
}

void LocalGame::addPlayer(const Character &character)
{
    m_playersDataList.push_back(character);
}

void LocalGame::clearPlayers()
{
    m_playersDataList.clear();
}

void LocalGame::run()
{
    const Vector viewSize = m_window->getViewSize();
    const Viewport &viewport = m_window->getViewport();
    const ScaledPixel sp(viewSize.x);

    Log::info(Format::format("Starting local game in {}, {} mode, "
                             "{} round(s) to win", m_arena,
        (m_rules.getMode() == FightRules::Mode::Versus) ? "versus" : "default",
        +m_rules.getRoundsToWin()));

    //Resource managers
    TextureManager textureManager;
    FontManager fontManager;
    SoundBufferManager soundBufferManager;

    AmbientPlayer ambient(m_configuration->outputAmbientVolume());

    SoundEngine soundEngine;
    soundEngine.loadSoundEffects("audio/sfx/sfx.xml");
    soundEngine.setManager(soundBufferManager);
    soundEngine.setVolume(m_configuration->outputSoundVolume());

    //Interface
    EndTab endDialog(fontManager.getDisplay(sp(24)),
                     textureManager, soundEngine, viewSize.x);

    PauseTab pauseDialog(fontManager.getDisplay(sp(54)),
                         fontManager.getDisplay(sp(24)),
                         textureManager, soundEngine, viewSize.x);
    pauseDialog.setPosition((viewSize.x - pauseDialog.getWidth()) / 2.f,
                            (viewSize.y - pauseDialog.getHeight()) * .4f);

    RoundAnnouncer announcer(viewSize);
    announcer.setSoundEngine(soundEngine);
    announcer.setSkin(fontManager.getDisplay(sp(106)),
                      Color(255, 255, 255), Color(50, 93, 151));

    FightInterface fightInterface(fontManager.getDisplay(sp(24)),
                                  fontManager.getDefault(sp(14)),
                                  fontManager.getDefault(sp(10)),
                                  viewSize.x);

    RoundResultInfo roundResultInfo(viewSize);
    roundResultInfo.setResources(fontManager.getDisplay(sp(86)),
                                 textureManager);

    //Fight controller
    LocalFightController fightController(textureManager, ambient, soundEngine,
        ClientFightScene::DisplayOptions(m_configuration->getCameraMode(),
                                        m_configuration->getEnableCameraShake(),
                                        m_configuration->getShowHitboxes()),
        viewSize.x);

    fightController.attach(announcer, fightInterface, roundResultInfo,
                            pauseDialog, endDialog);

    fightController.initialize(m_arena, m_rules, m_playersDataList,
                        (*m_configuration), m_gameEngine->getJoystickManager());

    const Camera interfaceCamera(Box(0.f, 0.f, viewSize.x, viewSize.y));

    OptionalFpsCounter fpsCounter(!m_configuration->getShowFps() ? nullptr :
                                  &(fontManager.getDefault(sp(18))));
    fpsCounter.setPosition(viewSize.x - sp(70.f), sp(5.f));

    //Start the music
    PlaylistPlayer::nextSong();

    //Start screen loop
    Timer time;
    SDL_Event event;
    while (1)
    {
        //Manage time
        const float frameTime = time.getElapsed();
        time.reset();

        //Manage events
        while (m_window->pollEvent(event))
        {
            switch (event.type)
            {
                case SDL_QUIT: //Application closed
                    m_gameEngine->exit();
                    return;
                break;

                case SDL_WINDOWEVENT:
                    if (event.window.event == SDL_WINDOWEVENT_RESIZED)
                        m_window->resizeView();
                break;

                case SDL_JOYDEVICEADDED:
                    m_gameEngine->getJoystickManager()
                                 .loadJoystick(event.jdevice.which);
                break;

                case SDL_JOYDEVICEREMOVED:
                    m_gameEngine->getJoystickManager()
                                 .removeJoystick(event.jdevice.which);
                    m_configuration->updatePlayerDevices(event.jdevice.which);
                break;

                case SDL_KEYDOWN: //Key pressed
                    //Pause handling
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                        fightController.togglePause();
                break;

                case SDL_MOUSEBUTTONUP: //Mouse released
                {
                    const Vector mouseCoords = viewport.project(event.button.x,
                                                                event.button.y);
                    endDialog.mouseRelease(mouseCoords);
                    pauseDialog.mouseRelease(mouseCoords);
                }
                break;

                case SDL_MOUSEMOTION: //Mouse moved
                {
                    const Vector mouseCoords = viewport.project(event.button.x,
                                                                event.button.y);
                    endDialog.mouseMove(mouseCoords);
                    pauseDialog.mouseMove(mouseCoords);
                }
                break;

                default:
                break;
            }

            //Process key events
            fightController.keyInput(event);
        }

        //End dialog result
        if (endDialog.chosen())
        {
            if (endDialog.result() == EndTab::Result::Replay)
                fightController.reset(); //Restart the fight
            else if (endDialog.result() == EndTab::Result::Exit)
            {
                PlaylistPlayer::nextSong();
                m_gameEngine->initMainMenu();
                return;
            }
        }

        //Pause dialog result
        if (pauseDialog.chosen())
        {
            const PauseTab::Result result = pauseDialog.result();

            if (result == PauseTab::Result::Resume)
                fightController.togglePause();
            else if (result == PauseTab::Result::Restart)
            {
                fightController.reset();
                fightController.togglePause();
            }
            else if (result == PauseTab::Result::Exit)
            {
                PlaylistPlayer::nextSong();
                m_gameEngine->initMainMenu();
                return;
            }
        }

        //Update fight scene
        fightController.update(frameTime);

        //Update sounds & interface
        soundEngine.update();
        fightInterface.update();
        fpsCounter.tick();

        //Display
            //Clear
        m_window->clear();

        fightController.draw(frameTime, viewSize);

        //Interface part
        interfaceCamera.look(viewSize.x, viewSize.y);

        fpsCounter.draw();
        fightInterface.draw();
        roundResultInfo.draw();
        announcer.draw();

        endDialog.draw();
        pauseDialog.draw();

            //Render
        m_window->flush();
    }
}
