/*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 "ArenaChooser.hpp"
#include "Engines/Resources/TextureManager.hpp"
#include "ArenaPreviewCallback.hpp"

ArenaChooser::ArenaChooser(Font &textFont, TextureManager &manager,
                           const SkinSound &sounds, std::size_t rows,
                           float rowWidth, float rowHeight, float spacing,
                           const Color &color) :
Tab(rowWidth, static_cast<float>(rows + 1) * (rowHeight + spacing) + rowHeight,
    color), m_selectedButton(nullptr), m_previewCallback(nullptr),
m_textureManager(&manager), m_font(&textFont), m_sounds(sounds),
m_currentLevel(0), m_targetLevel(0), m_currentOffset(0.f), m_rows(rows),
m_rowHeight(rowHeight), m_spacing(spacing)
{

}

void ArenaChooser::draw()
{
    //Update matrix
    Drawable::pushMatrix();
    Drawable::updateMatrix();

    m_background.draw();

    for (TextButton &arenaButton : m_arenaButtons)
    {
        //Only show arena buttons that should be visible
        if (isShown(arenaButton))
            arenaButton.draw();
    }

    m_upArrow.draw();
    m_downArrow.draw();

    Drawable::popMatrix();
}

void ArenaChooser::update(float frameTime)
{
    if (m_currentLevel == m_targetLevel) //Not scrolling
        return;

    constexpr float scrollingSpeed = 1.f; ///< px / ms
    const float moveStep = frameTime * scrollingSpeed;
    const bool incrementing = (m_targetLevel > m_currentLevel);
    const float offsetTarget =
        static_cast<float>(m_targetLevel) * (m_rowHeight + m_spacing);

    m_currentOffset += incrementing ? moveStep : -moveStep;

    if ((incrementing && m_currentOffset >= offsetTarget) ||
        (!incrementing && m_currentOffset <= offsetTarget))
    {
        m_currentOffset = offsetTarget;
        m_currentLevel = m_targetLevel;
    }

    updatePosition();
}

void ArenaChooser::updatePosition()
{
    for (std::size_t i = 0; i < m_arenaButtons.size(); i++)
    {
        m_arenaButtons[i].setYPosition(
            static_cast<float>((i+1)) * (m_rowHeight + m_spacing) -
            m_currentOffset);
    }
}

void ArenaChooser::initialize(
    const std::vector<std::pair<std::string, std::string>> &data,
    ArenaPreviewCallback &callback)
{
    m_selectedButton = nullptr;
    m_previewCallback = &callback;
    m_currentLevel = 0;
    m_targetLevel = 0;
    m_currentOffset = 0.f;

    Texture &ui = m_textureManager->getTexture("gfx/ui/ui.png");

    const Sprite ui_default(ui,
        Box(266.f / 512.f, 0.f, 246.f / 512.f, 71.f / 512.f));
    const Sprite ui_hover(ui,
        Box(266.f / 512.f, 75.f / 512.f, 246.f / 512.f, 71.f / 512.f));
    const Sprite arrow_default(ui,
        Box(266.f / 512.f, 150.f / 512.f, 246.f / 512.f, 71.f / 512.f));

    const SkinBox arrowSkin(Tab::getWidth(), m_rowHeight,
                            arrow_default, ui_hover);
    const SkinBox rowSkin(Tab::getWidth(), m_rowHeight, ui_default,
                          ui_hover, ui_hover);

    m_upArrow = TextButton("<", arrowSkin, m_sounds);
    m_upArrow.setTextColor(Color(255, 255, 255));
    m_upArrow.setTextFont(*m_font);
    m_upArrow.setPosition((Tab::getWidth() - m_upArrow.getWidth()) / 2.f, 0.f);

    m_downArrow = TextButton(">", arrowSkin, m_sounds);
    m_downArrow.setTextColor(Color(255, 255, 255));
    m_downArrow.setTextFont(*m_font);
    m_downArrow.setPosition((Tab::getWidth() - m_downArrow.getWidth()) / 2.f,
                            Tab::getHeight() - m_downArrow.getHeight());

    m_arenaButtons.clear();
    m_arenaButtons.reserve(data.size());
    for (std::size_t i = 0; i < data.size(); i++)
    {
        const std::string &arenaName = data[i].first;

        TextButton arenaButton = TextButton(arenaName, rowSkin, m_sounds);
        arenaButton.setTextColor(Color(255, 255, 255));
        arenaButton.setTextFont(*m_font);
        arenaButton.setPosition(
            (Tab::getWidth() - arenaButton.getWidth()) / 2.f,
            static_cast<float>((i+1)) * (m_rowHeight + m_spacing));

        m_arenaButtons.push_back(arenaButton);
    }

    //Initialize selection
    if (!m_arenaButtons.empty())
    {
        m_arenaButtons[0].select(true);
        m_arenaButtons[0].setTextColor(Color(255, 255, 255));
        m_selectedButton = &m_arenaButtons[0];

        m_previewCallback->previewChanged(0);
    }
}

bool ArenaChooser::mouseRelease(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);

    if (m_arenaButtons.empty())
        return Widget::contains(localMouse);

    if (m_upArrow.mouseRelease(localMouse) && m_targetLevel > 0)
        m_targetLevel--;
    else if (m_downArrow.mouseRelease(localMouse) &&
             m_arenaButtons.size() > m_rows &&
             m_targetLevel < m_arenaButtons.size() - m_rows)
        m_targetLevel++;

    for (std::size_t i = 0; i < m_arenaButtons.size(); i++)
    {
        TextButton &arenaButton = m_arenaButtons[i];
        if (isClickable(arenaButton))
        {
            if (arenaButton.mouseRelease(localMouse))
            {
                m_selectedButton->select(false);
                m_selectedButton = &arenaButton;
                arenaButton.select(true);
                m_previewCallback->previewChanged(i);
            }
        }
    }

    return Widget::contains(localMouse);
}

bool ArenaChooser::mouseClick(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);
    return Widget::contains(localMouse);
}

bool ArenaChooser::mouseMove(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);

    m_upArrow.mouseMove(localMouse);
    m_downArrow.mouseMove(localMouse);

    for (TextButton &arenaButton : m_arenaButtons)
    {
        if (isClickable(arenaButton))
            arenaButton.mouseMove(localMouse);
    }

    return Widget::contains(localMouse);
}

bool ArenaChooser::isShown(const TextButton &button) const
{
    return button.getYPosition() + button.getHeight() >
           m_upArrow.getYPosition() + m_upArrow.getHeight() &&
        button.getYPosition() < m_downArrow.getYPosition();
}

bool ArenaChooser::isClickable(const TextButton &button) const
{
    return button.getYPosition() >
           m_upArrow.getYPosition() + m_upArrow.getHeight() &&
        button.getYPosition() + button.getHeight() < m_downArrow.getYPosition();
}
