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

#include <string>
#include <cmath>

ContinuousChooser::ContinuousChooser() :
Widget(), m_startValue(0.f), m_endValue(100.f), m_value(50.f), m_enabled(true)
{

}

ContinuousChooser::ContinuousChooser(const ContinuousChooserSkin &skin,
                                     float startValue, float endValue,
                                     float initialValue) :
Widget(), m_skin(skin), m_startValue(0.f), m_endValue(100.f),
m_value(50.f), m_enabled(true)
{
    if (endValue >= startValue)
    {
        m_startValue = startValue;
        m_endValue = endValue;
    }

    if (initialValue >= startValue && initialValue <= endValue)
        m_value = initialValue;

    m_bar = Polygon::rectangle(m_skin.barSize.x, m_skin.barSize.y);
    m_bar.setBorderSize(m_skin.borderSize);
    m_bar.setPosition(m_skin.hitMargin,
                      (m_skin.handleSize.y - m_skin.barSize.y) / 2.f);

    m_handle = Polygon::rectangle(m_skin.handleSize.x, m_skin.handleSize.y,
                                  m_skin.handleColor);
    m_handle.setBorderSize(m_skin.borderSize);

    m_valueText = Text("?", (*m_skin.font));
    m_valueText.setColor(m_skin.labelColor);
    m_valueText.setPosition(m_skin.hitMargin + 1.15f * m_skin.barSize.x,
        (m_skin.handleSize.y - m_valueText.getHeight()) / 2.f);

    updateValue(m_skin.hitMargin + m_skin.barSize.x *
                (m_value - m_startValue) / (m_endValue - m_startValue));
}

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

    m_bar.draw();
    m_handle.draw();
    if (m_enabled)
        m_valueText.draw();

    Drawable::popMatrix();
}

bool ContinuousChooser::mouseRelease(const Vector &mouseCoords)
{
    if (!m_enabled)
        return false;

    setFocus(false);
    return Widget::contains( Widget::localCoords(mouseCoords) );
}

bool ContinuousChooser::mouseClick(const Vector &mouseCoords)
{
    if (!m_enabled)
        return false;

    Vector localMouse = Widget::localCoords(mouseCoords);
    bool clicked = Widget::contains(localMouse);
    if (clicked)
    {
        setFocus(true);
        updateValue(localMouse.x);
    }

    return clicked;
}

bool ContinuousChooser::mouseMove(const Vector &mouseCoords)
{
    if (!m_enabled)
        return false;

    Vector localMouse = Widget::localCoords(mouseCoords);

    if (isFocused())
        updateValue(localMouse.x);

    return Widget::contains(localMouse);
}

float ContinuousChooser::getValue() const
{
    return m_value;
}

void ContinuousChooser::setEnabled(bool enabled)
{
    m_enabled = enabled;
    m_handle.setUniformColor(m_enabled ? m_skin.handleColor
                                       : m_skin.disabledHandleColor);
}

float ContinuousChooser::getWidth() const
{
    return m_skin.barSize.x + m_skin.hitMargin * 2.f;
}

float ContinuousChooser::getHeight() const
{
    return m_skin.handleSize.y;
}

void ContinuousChooser::updateValue(float localXMouse)
{
    float barX = localXMouse - m_skin.hitMargin;
    if (barX < 0.f) barX = 0.f;
    else if (barX > m_skin.barSize.x) barX = m_skin.barSize.x;

    const float value = floorf((barX / m_skin.barSize.x) *
        (m_endValue - m_startValue) + m_startValue + .5f);

    if (value >= m_startValue && value <= m_endValue)
    {
        m_handle.setXPosition(m_skin.hitMargin + barX -
                              m_skin.handleSize.x / 2.f);

        m_value = value;
        m_valueText.setText(std::to_string(static_cast<int>(m_value)));
    }
}
