/*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 "Chooser.hpp"

Chooser::Chooser() : Widget(), m_selection(0), m_opened(false) {}

Chooser::Chooser(const std::vector<std::string> &data,
                 std::size_t selection) :
Chooser()
{
    setData(data, selection);
}

Chooser::Chooser(const TextButtonSkin &skin,
                 const std::vector<std::string> &data, std::size_t selection) :
Chooser()
{
    setSkin(skin);
    setData(data, selection);
}

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

    if (!m_opened && m_choicesList.size() > m_selection)
        m_choicesList[m_selection].draw();
    else
    {
        for (TextButton &choice : m_choicesList)
            choice.draw();
    }

    Drawable::popMatrix();
}

bool Chooser::mouseRelease(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);
    bool released = false;

    if (m_choicesList.size() > m_selection &&
        m_choicesList[m_selection].mouseRelease(localMouse))
    {
        m_opened = !m_opened;
        released = true;
    }
    else if (m_opened)
    {
        for (std::size_t i = 0; i < m_choicesList.size(); i++)
        {
            if (i != m_selection && m_choicesList[i].mouseRelease(localMouse))
            {
                setSelection(i);
                released = true;
                break;
            }
        }
    }

    if (!released)
        m_opened = false;

    return released;
}

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

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

    if (!m_opened && m_choicesList.size() > m_selection)
        m_choicesList[m_selection].mouseMove(localMouse);
    else
    {
        for (TextButton &choice : m_choicesList)
            choice.mouseMove(localMouse);
    }

    //Chooser focus handling
    if ( Widget::contains(localMouse) )
    {
        setFocus(true);
        return true;
    }

    setFocus(false);
    return false;
}

void Chooser::setData(const std::vector<std::string> &data,
                      std::size_t selection)
{
    std::vector<std::pair<Sprite, std::string>> illustrated;
    illustrated.reserve(data.size());

    for (const std::string &choice : data)
        illustrated.emplace_back(Sprite(), choice);

    setIllustratedData(illustrated, selection);
}

void Chooser::setIllustratedData(
    const std::vector<std::pair<Sprite, std::string>> &illustrated,
    std::size_t selection)
{
    if (selection >= illustrated.size())
        return;

    m_choicesList.clear();
    m_choicesList.reserve(illustrated.size());

    float yPosition = 0.f;
    float skinWidth = 0.f;
    for (const std::pair<Sprite, std::string> &item : illustrated)
    {
        m_choicesList.emplace_back(item.second, m_choiceSkin);
        m_choicesList.back().setAvatar(item.first);
        m_choicesList.back().setYPosition(yPosition);
        yPosition += m_choicesList.back().getHeight();

        //Check for expanded text buttons
        if (m_choicesList.back().getWidth() > skinWidth)
            skinWidth = m_choicesList.back().getWidth();
    }

    setSelection(selection);

    //Re-draw choices with expanded skin if needed
    constexpr float skinEpsilon = .1f;
    if (skinWidth > m_choiceSkin.skin.box.width + skinEpsilon)
    {
        m_choiceSkin.skin.box.width = skinWidth;
        setIllustratedData(illustrated, selection);
    }
}

void Chooser::setSkin(const TextButtonSkin &skin)
{
    m_choiceSkin = skin;
}

const TextButtonSkin &Chooser::getExpandedSkin() const
{
    return m_choiceSkin;
}

void Chooser::setSelection(std::size_t selection)
{
    if (m_choicesList.size() > selection)
    {
        m_selection = selection;

        for (std::size_t i = 0; i < m_choicesList.size(); i++)
            m_choicesList[i].select((i == m_selection));

        setYOrigin(static_cast<float>(m_selection) *
                   m_choiceSkin.skin.box.height);

        //Close the Chooser
        m_opened = false;
    }
}

std::size_t Chooser::getSelection() const
{
    return m_selection;
}

std::string Chooser::getSelectionStr() const
{
    return (m_choicesList.size() > m_selection) ?
        m_choicesList[m_selection].getText() : "?";
}

bool Chooser::isOpened() const
{
    return m_opened;
}

float Chooser::getWidth() const
{
    return m_choiceSkin.skin.box.width;
}

float Chooser::getHeight() const
{
    return (!m_opened) ? m_choiceSkin.skin.box.height :
        m_choiceSkin.skin.box.height * static_cast<float>(m_choicesList.size());
}
