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

#include "ArenaChooser.hpp"
#include "Graphics/Camera.hpp"
#include "Graphics/Drawable/Sprite.hpp"
#include "Engines/Resources/JoystickManager.hpp"
#include "Engines/Sound/PlaylistPlayer.hpp"
#include "Engines/Sound/SoundEngine.hpp"
#include "GUI/SectionHeader.hpp"
#include "GUI/PlaylistNotifier.hpp"
#include "Tools/ScaledPixel.hpp"
#include "Tools/Timer.hpp"
#include "Tools/I18n.hpp" ///< _ macro for i18n
#include "Tools/BuildValues.hpp" ///< Generated at build time
#include <pugixml.hpp>

ArenaMenu::ArenaMenu(GameEngine &gameEngine) : m_currentArena(0),
m_gameEngine(&gameEngine), m_window(&(gameEngine.getWindow())),
m_configuration(&(gameEngine.getConfiguration()))
{

}

void ArenaMenu::previewChanged(std::size_t arenaIx)
{
    if (arenaIx >= m_arenas.size())
        return;

    m_previewWorld = PhysicsWorld();

    m_previewArena = ArenaTools::load(m_arenas[arenaIx].second);
    m_previewArena.applyToPhysics(m_previewWorld);
    m_previewArena.subscribePlatforms(m_previewWorld);

    m_previewDrawer.attachTo(m_previewArena, m_textureManager);

    m_previewBox = Box(0.f, 0.f, m_previewArena.getWidth(),
                       m_previewArena.getHeight());

    const Vector viewSize = m_window->getViewSize();
    const ScaledPixel sp(viewSize.x);

    const float targetWidth = viewSize.x * .5f;
    const float targetHeight = targetWidth *
        (m_previewArena.getHeight() / m_previewArena.getWidth());

    m_previewTargetBox = Box(viewSize.x - targetWidth - sp(30.f),
                             (viewSize.y - targetHeight) / 2.f,
                             targetWidth, targetHeight);
    m_previewBackground = Polygon::rectangle(
                                        m_previewTargetBox.width + sp(20.f),
                                        m_previewTargetBox.height + sp(20.f),
                                        Color(66, 112, 174),
                                        Color(0, 0, 0));
    m_previewBackground.setBorderSize(0.f);
    m_previewBackground.setPosition(m_previewTargetBox.left - sp(10.f),
                                    m_previewTargetBox.top - sp(10.f));

    m_currentArena = arenaIx;
}

void ArenaMenu::run()
{
    fetchNames("cfg/arenas.xml");

    const Vector viewSize = m_window->getViewSize();
    const Viewport &viewport = m_window->getViewport();
    const ScaledPixel sp(viewSize.x);

    SoundEngine soundEngine;
    soundEngine.setManager(m_soundBufferManager);
    soundEngine.setVolume(m_configuration->outputSoundVolume());

    //Background + Header
    Sprite background(m_textureManager.getTexture("gfx/ui/nagano.png"),
                      Box(0.f, 0.f, 1920.f / 2048.f, 1080.f / 2048.f));
    background.setScale(viewSize.x / background.getWidth(),
                        viewSize.y / background.getHeight());

    SectionHeader header(_(ArenaSelectionTitle),
                         _(ArenaSelectionSubtitle),
                         m_textureManager, m_fontManager,
                         soundEngine, viewSize);
    //Arena Chooser
    const Sprite ui_default(m_textureManager.getTexture("gfx/ui/ui.png"),
        Box(266.f / 512.f, 150.f / 512.f, 246.f / 512.f, 71.f / 512.f));
    const Sprite ui_hover(m_textureManager.getTexture("gfx/ui/ui.png"),
        Box(266.f / 512.f, 75.f / 512.f, 246.f / 512.f, 71.f / 512.f));

    SkinBox skin(sp(225.f), sp(50.f), ui_default, ui_hover);
    SkinSound sounds(soundEngine, "audio/ui/menuFocus.ogg",
                     "audio/ui/menuClick.ogg");

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

    ArenaChooser chooser(m_fontManager.getDisplay(sp(22)), m_textureManager,
        sounds, 6, sp(225.f), sp(60.f), sp(5.f), Color(0, 0, 0, 0));
    chooser.setPosition(sp(30.f),
        (viewSize.y + header.getHeight() - chooser.getHeight()) / 2.f);
    chooser.initialize(m_arenas, (*this));

    TextButton launchFight(_(ArenaSelectionValidate), skin, sounds);
    launchFight.setTextFont(m_fontManager.getDisplay(sp(24)));
    launchFight.setTextColor(Color(255, 255, 255));
    launchFight.setPosition(viewSize.x - launchFight.getWidth() - sp(20.f),
                            viewSize.y - launchFight.getHeight() - sp(30.f));

    //Playlist handling
    PlaylistNotifier notifier(viewSize.x);
    notifier.setFonts(m_fontManager.getDefault(sp(20)),
                      m_fontManager.getDefault(sp(14)));
    notifier.setPosition(viewSize.x - notifier.getWidth() - sp(20.f),
                         viewSize.y - notifier.getHeight() - sp(20.f));

    PlaylistPlayer::setNotifier(&notifier);

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

        //Manage events
        while (m_window->pollEvent(event))
        {
            switch (event.type)
            {
                case SDL_QUIT: //Application closed
                    m_gameEngine->exit();
                    PlaylistPlayer::setNotifier(nullptr);
                    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_MOUSEBUTTONUP: //Mouse released
                {
                    const Vector mouseCoords = viewport.project(event.button.x,
                                                                event.button.y);
                    if (header.backRelease(mouseCoords))
                    {
                        m_gameEngine->initCharactersMenu();
                        PlaylistPlayer::setNotifier(nullptr);
                        return;
                    }

                    chooser.mouseRelease(mouseCoords);

                    if (launchFight.mouseRelease(mouseCoords) &&
                        m_previewArena.getWidth() > 0.f &&
                        m_currentArena < m_arenas.size())
                    {
                        m_gameEngine->confLocalGame_arena(
                            m_arenas[m_currentArena].second);
                        m_gameEngine->initLocalGame();

                        PlaylistPlayer::setNotifier(nullptr);
                        return;
                    }
                }
                break;

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

                case SDL_KEYDOWN:
                    //Escape was pressed, back to character selection
                    if (event.key.keysym.sym == SDLK_ESCAPE)
                    {
                        //Back to characters menu.
                        m_gameEngine->initCharactersMenu();
                        PlaylistPlayer::setNotifier(nullptr);
                        return;
                    }

                break;

                default:
                break;
            }
        }

        soundEngine.update();
        notifier.update();
        chooser.update(frameTime);

        //Preview physics update
        m_previewWorld.update(frameTime);

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

            //Draw
        background.draw();
        m_previewBackground.draw();

        Camera::look(m_previewBox, m_previewTargetBox);
        m_previewDrawer.draw();

        defaultCamera.look(viewSize.x, viewSize.y);

        chooser.draw();
        launchFight.draw();

        header.draw();

            //Playlist notifier
        notifier.draw();

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

void ArenaMenu::fetchNames(const std::string &xmlRelPath)
{
    m_arenas.clear();

    //pugixml initialization
    //Locate absolute path to xml data
    std::string xmlPath = BuildValues::data(xmlRelPath);

    pugi::xml_document xmlFile;
    if (!xmlFile.load_file(xmlPath.c_str()))
        return;

    pugi::xml_node arenaNode;
    for (arenaNode = xmlFile.child("main").child("arena");
         arenaNode;
         arenaNode = arenaNode.next_sibling("arena"))
    {
        m_arenas.push_back(std::make_pair(
            std::string(arenaNode.attribute("name").value()),
            std::string(arenaNode.attribute("pathname").value())));
    }
}
