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

#include "OptionsTab.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/SoundEngine.hpp"
#include "GUI/Widget/TextButton.hpp"
#include "GUI/SectionHeader.hpp"
#include "GUI/PlaylistNotifier.hpp"
#include "Structs/JoystickData.hpp"
#include "Tools/ScaledPixel.hpp"
#include "Tools/I18n.hpp" ///< _ macro for i18n

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

}

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

    TextureManager textureManager;
    FontManager fontManager;
    SoundBufferManager soundBufferManager;

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

    //Background + Header
    Sprite background(textureManager.getTexture("gfx/ui/title.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(_(OptionsTitle), _(OptionsSubtitle),
                         textureManager, fontManager, soundEngine, viewSize);

    //Content
    OptionsTab optionsTab((*m_configuration), fontManager, textureManager,
                          soundEngine, m_window->getDisplayMode(), viewSize.x);
    optionsTab.setPosition((viewSize.x - optionsTab.getWidth()) / 2.f,
                           viewSize.y * .23f);
    optionsTab.setDisplaySizes(m_window->getDisplayModeSizes());

    for (const JoystickData &joystickData :
         m_gameEngine->getJoystickManager().getLoadedJoysticksData())
        optionsTab.addJoystickBinder(joystickData);

    //Playlist handling
    PlaylistNotifier notifier(viewSize.x);
    notifier.setFonts(fontManager.getDefault(sp(20)),
                      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
    SDL_Event event;
    while (1)
    {
        //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:
                {
                    int instanceID = m_gameEngine->getJoystickManager()
                                         .loadJoystick(event.jdevice.which);
                    if (instanceID != JOYSTICK_INSTANCE_INVALID)
                        optionsTab.addJoystickBinder(
                            m_gameEngine->getJoystickManager()
                                         .getJoystickData(instanceID));
                }
                break;

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

                case SDL_MOUSEBUTTONDOWN: //Mouse pressed
                    optionsTab.mouseClick(viewport.project(event.button.x,
                                                           event.button.y));
                break;

                case SDL_MOUSEBUTTONUP: //Mouse released
                {
                    const Vector mouseCoords = viewport.project(event.button.x,
                                                                event.button.y);
                    optionsTab.mouseRelease(mouseCoords);
                    if (optionsTab.displayModeApplied())
                    {
                        m_window->resizeWindow(
                            m_configuration->getDisplayLayout());

                        //Reload
                        m_gameEngine->initOptions();
                        PlaylistPlayer::setNotifier(nullptr);
                        return;
                    }
                    else if (optionsTab.refreshRateUpdated())
                    {
                        m_window->setVSyncMode(m_configuration->getVSyncMode());
                        m_window->setFrameRate(static_cast<float>(
                            m_configuration->getFrameRate()));
                    }
                    else if (optionsTab.volumeUpdated())
                    {
                        PlaylistPlayer::setVolume(
                            m_configuration->outputMusicVolume());
                        soundEngine.setVolume(
                            m_configuration->outputSoundVolume());
                    }
                    else if (optionsTab.localeUpdated())
                    {
                        I18n::loadEntries(m_configuration->getLocale());

                        //Reload
                        m_gameEngine->initOptions();
                        PlaylistPlayer::setNotifier(nullptr);
                        return;
                    }

                    if (header.backRelease(mouseCoords))
                    {
                        m_gameEngine->initMainMenu();
                        PlaylistPlayer::setNotifier(nullptr);
                        return;
                    }
                }
                break;

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

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

                break;

                default:
                break;
            }

            //Key event?
            optionsTab.keyEvent(event);
        }

        //Update sounds
        soundEngine.update();
        notifier.update();

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

            //Draw
        background.draw();
        header.draw();
        optionsTab.draw();

            //Playlist notifier
        notifier.draw();

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