/*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 "Dialog.hpp"
#include "Engines/Resources/TextureManager.hpp"
#include "Engines/Resources/FontManager.hpp"
#include "Engines/Sound/SoundEngine.hpp"
#include "Structs/Vector.hpp"

////////////////
///DialogSkin///
////////////////
DialogSkin::DialogSkin(float dialogWidth, float dialogHeight,
                       const Color &dialogColor, const Color &msgColor,
                       Font &msgFont, const TextButtonSkin &skin) :
width(dialogWidth), height(dialogHeight),
backgroundColor(dialogColor), messageColor(msgColor),
messageFont(&msgFont), buttonSkin(skin)
{

}

DialogSkin DialogSkin::getDefault(TextureManager &textureManager,
                                  FontManager &fontManager,
                                  SoundEngine &soundEngine)
{
    Font &messageFont = fontManager.getDefault(20);
    Font &buttonFont = fontManager.getDefault(15);

    const Sprite buttonDefault(textureManager.getTexture("gfx/ui/ui.png",
                                                Texture::MinMagFilter::Nearest),
        Box(20.f / 512.f, 128.f / 512.f, 1.f / 512.f, 1.f / 512.f) );

    const Sprite buttonHover(textureManager.getTexture("gfx/ui/ui.png"),
        Box(0.f, 128.f / 512.f, 1.f / 512.f, 1.f / 512.f));

    const Color textColor(255, 255, 255);

    const SkinBox skin(120.f, 30.f, buttonDefault, buttonHover);
    const SkinSound sounds(soundEngine, "audio/ui/menuFocus.ogg",
                           "audio/ui/menuClick.ogg");

    return DialogSkin(460.f, 80.f, Color(40, 68, 106), textColor, messageFont,
                      TextButtonSkin(buttonFont, textColor, skin, sounds));
}

///////////////
///DialogTab///
///////////////
DialogTab::DialogTab(const DialogSkin &skin, const std::string &cancelText) :
Tab(skin.width, skin.height, skin.backgroundColor),
m_cancelButton(cancelText, skin.buttonSkin), m_receiver(nullptr)
{
    //Tweak the background to add a dialog border
    m_background.setUniformColor(skin.backgroundColor, Color(0, 0, 0, 60));
    m_background.setBorderSize(5.f);

    if (skin.messageFont != nullptr)
        m_messageField.setFont(*(skin.messageFont));

    m_messageField.setColor(skin.messageColor);
    m_messageField.setPosition(10.f, 10.f);

    m_cancelButton.setPosition(getWidth() - m_cancelButton.getWidth() - 10.f,
                               getHeight() - m_cancelButton.getHeight() - 10.f);
}

void DialogTab::setMessage(const std::string &message)
{
    m_messageField.setText(message);
}

void DialogTab::setCallbackReceiver(DialogCallbackReceiver *receiver)
{
    m_receiver = receiver;
}

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

    m_background.draw();
    m_messageField.draw();
    m_cancelButton.draw();

    Drawable::popMatrix();
}

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

    if (m_cancelButton.mouseRelease(localMouse) && m_receiver != nullptr)
        m_receiver->dialogCancelledCallback();

    return Widget::contains(localMouse);
}

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

bool DialogTab::mouseMove(const Vector &mouseCoords)
{
    Vector localMouse = Widget::localCoords(mouseCoords);
    m_cancelButton.mouseMove(localMouse);
    return Widget::contains(localMouse);
}

////////////
///Dialog///
////////////
Dialog::Dialog(const DialogSkin &skin, const std::string &cancelText,
               const Vector &viewSize, bool cancelWithMouseRelease) :
DialogCallbackReceiver(),
m_viewShadow(Polygon::rectangle(viewSize.x, viewSize.y, Color(0, 0, 0, 100))),
m_dialogTab(skin, cancelText),
m_cancelWithMouseRelease(cancelWithMouseRelease), m_receiver(nullptr)
{
    m_viewShadow.setBorderSize(0.f);

    m_dialogTab.setCallbackReceiver(this);
    m_dialogTab.setPosition((viewSize.x - m_dialogTab.getWidth()) / 2.f,
                            (viewSize.y - m_dialogTab.getHeight()) / 2.f);
}

void Dialog::draw()
{
    if (active()) //Draw only when active
    {
        m_viewShadow.draw();
        m_dialogTab.draw();
    }
}

void Dialog::setMessage(const std::string &message)
{
    m_dialogTab.setMessage(message);
}

void Dialog::setCallbackReceiver(DialogCallbackReceiver *receiver)
{
    m_receiver = receiver;
}

void Dialog::dialogCancelledCallback()
{
    if (m_receiver != nullptr) //Forward the callback if any receiver listens
        m_receiver->dialogCancelledCallback();
    else //Otherwise, directly handle the dialog hiding
        hide();
}

bool Dialog::mouseRelease(const Vector &mouseCoords)
{
    if (active())
    {
        if (m_cancelWithMouseRelease)
            dialogCancelledCallback();

        return m_dialogTab.mouseRelease(mouseCoords);
    }

    return false;
}

bool Dialog::mouseClick(const Vector &mouseCoords)
{
    return active() ? m_dialogTab.mouseClick(mouseCoords) : false;
}

bool Dialog::mouseMove(const Vector &mouseCoords)
{
    return active() ? m_dialogTab.mouseMove(mouseCoords) : false;
}

bool Dialog::cancel()
{
    if (active())
    {
        dialogCancelledCallback();
        return true;
    }

    return false;
}

void Dialog::show()
{
    m_dialogTab.setFocus(true);
}

void Dialog::hide()
{
    m_dialogTab.setFocus(false);
}

bool Dialog::active() const
{
    return m_dialogTab.isFocused();
}
