/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2021 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Tests.hpp"
#include <TGUI/Widgets/Button.hpp>
#include <TGUI/Widgets/Panel.hpp>
#include <iostream>

using namespace tgui::bind_functions;
using tgui::Layout;
using tgui::Layout2d;

TEST_CASE("[Layouts]")
{
    SECTION("constants")
    {
        Layout l1;
        Layout l2(-20.3f);
        Layout l3{"60"};

        Layout2d l4;
        Layout2d l5({10.f, {" 50 "}});
        Layout2d l6(".2", "-3.5");

        REQUIRE(l1.getValue() == 0);
        REQUIRE(l2.getValue() == -20.3f);
        REQUIRE(l3.getValue() == 60);

        REQUIRE(l4.getValue() == tgui::Vector2f(0, 0));
        REQUIRE(l5.getValue() == tgui::Vector2f(10, 50));
        REQUIRE(l6.getValue() == tgui::Vector2f(0.2f, -3.5f));

        REQUIRE(l1.isConstant());
        REQUIRE(l2.isConstant());
        REQUIRE(l3.isConstant());

        REQUIRE(l4.x.isConstant());
        REQUIRE(l4.y.isConstant());
        REQUIRE(l5.x.isConstant());
        REQUIRE(l5.y.isConstant());
        REQUIRE(l6.x.isConstant());
        REQUIRE(l6.y.isConstant());
    }

    SECTION("copying layouts")
    {
        Layout l1;
        Layout l2{2};
        Layout l3 = l2;
        Layout l4;
        Layout l5{0};
        Layout l6{"max(2,3)+1"};
        l5 = std::move(l6);

        REQUIRE(l1.getValue() == 0);
        REQUIRE(l3.getValue() == 2);
        REQUIRE(l4.getValue() == 0);
        REQUIRE(l5.getValue() == 4);

        REQUIRE(l1.toString() == "0");
        REQUIRE(l3.toString() == "2");
        REQUIRE(l4.toString() == "0");
        REQUIRE(l5.toString() == "max(2, 3) + 1");
    }

    SECTION("without strings")
    {
        SECTION("operators")
        {
            Layout l1{2};
            Layout l2(3);

            REQUIRE((l1 + l2).getValue() == 5);
            REQUIRE((l1 - l2).getValue() == -1);
            REQUIRE((l1 * l2).getValue() == 6);
            REQUIRE((l1 / l2).getValue() == 2.f/3.f);
            REQUIRE((-Layout{1}).getValue() == -1);
            REQUIRE((-Layout{-2}).getValue() == 2);

            REQUIRE((l1 + l2).toString() == "2 + 3");
            REQUIRE((l1 - l2).toString() == "2 - 3");
            REQUIRE((l1 * l2).toString() == "2 * 3");
            REQUIRE((l1 / l2).toString() == "2 / 3");
            REQUIRE((-Layout{1}).toString() == "0 - 1");
            REQUIRE((-Layout{-2}).toString() == "0 - -2");

            Layout2d l3{-6, 5};
            Layout2d l4(4, 5);
            Layout2d l5(6, 7);

            REQUIRE((l3 + l4).getValue() == tgui::Vector2f(-2, 10));
            REQUIRE((l3 - l4).getValue() == tgui::Vector2f(-10, 0));
            REQUIRE((-3 * l3).getValue() == tgui::Vector2f(18, -15));
            REQUIRE((l3 * 2).getValue() == tgui::Vector2f(-12, 10));
            REQUIRE((l3 / 2).getValue() == tgui::Vector2f(-3, 2.5f));
            REQUIRE((-l3).getValue() == tgui::Vector2f(6, -5));

            REQUIRE((l3 + l4).toString() == "(-6 + 4, 5 + 5)");
            REQUIRE((l3 - l4).toString() == "(-6 - 4, 5 - 5)");
            REQUIRE((-3 * l3).toString() == "(-3 * -6, -3 * 5)");
            REQUIRE((l3 * 2).toString() == "(-6 * 2, 5 * 2)");
            REQUIRE((l3 / 2).toString() == "(-6 / 2, 5 / 2)");
            REQUIRE((-l3).toString() == "(0 - -6, 0 - 5)");

            REQUIRE((Layout(5) + Layout(3) * Layout(2) - Layout(1)).getValue() == 10);
            REQUIRE((Layout(5) + Layout(3) * (Layout(2) - Layout(1))).getValue() == 8);
            REQUIRE(((Layout(5) + Layout(3)) * Layout(2) - Layout(1)).getValue() == 15);
            REQUIRE((Layout2d(5, 2) + Layout2d(3, -3) / Layout(2) - Layout2d(-1, 6)).getValue() == tgui::Vector2f(7.5f, -5.5f));
            REQUIRE(((Layout2d(5, 2) + Layout2d(3, -3)) / Layout(2) - Layout2d(-1, 6)).getValue() == tgui::Vector2f(5, -6.5f));

            REQUIRE((Layout(5) + Layout(3) * Layout(2) - Layout(1)).toString() == "(5 + (3 * 2)) - 1");
            REQUIRE((Layout(5) + Layout(3) * (Layout(2) - Layout(1))).toString() == "5 + (3 * (2 - 1))");
            REQUIRE(((Layout(5) + Layout(3)) * Layout(2) - Layout(1)).toString() == "((5 + 3) * 2) - 1");
            REQUIRE((Layout2d(5, 2) + Layout2d(3, -3) / Layout(2) - Layout2d(-1, 6)).toString() == "((5 + (3 / 2)) - -1, (2 + (-3 / 2)) - 6)");
            REQUIRE(((Layout2d(5, 2) + Layout2d(3, -3)) / Layout(2) - Layout2d(-1, 6)).toString() == "(((5 + 3) / 2) - -1, ((2 + -3) / 2) - 6)");
        }

        SECTION("bind functions")
        {
            SECTION("Widgets")
            {
                auto button1 = std::make_shared<tgui::Button>();
                button1->setSize(300, 50);
                button1->setPosition(40, 60);

                REQUIRE(!bindLeft(button1).isConstant());

                auto button2 = std::make_shared<tgui::Button>();
                button2->setPosition(bindLeft(button1), bindTop(button1));
                button2->setSize(bindWidth(button1), bindHeight(button1));
                REQUIRE(button2->getSize() == tgui::Vector2f(300, 50));
                REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));

                button2->setPosition(bindRight(button1), bindBottom(button1));
                REQUIRE(button2->getPosition() == tgui::Vector2f(340, 110));

                button2->setPosition(bindPosition(button1));
                button2->setSize(bindSize(button1));
                REQUIRE(button2->getSize() == tgui::Vector2f(300, 50));
                REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));

                button1->setSize(400, 40);
                button1->setPosition(60, 75);
                REQUIRE(button2->getSize() == tgui::Vector2f(400, 40));
                REQUIRE(button2->getPosition() == tgui::Vector2f(60, 75));

                button1->setSize(bindSize(button2)); // Binding each other only works when value is cached
                REQUIRE(button1->getSize() == tgui::Vector2f(400, 40));
                REQUIRE(button2->getSize() == tgui::Vector2f(400, 40));

                auto button3 = std::make_shared<tgui::Button>();
                auto panel = std::make_shared<tgui::Panel>();
                panel->setSize(200, 180);
                panel->setPosition(10, 25);
                button1->setSize(300, 50);
                button1->setPosition(40, 60);
                button2->setSize(400, 40);
                button2->setPosition(60, 75);
                panel->add(button1);
                panel->add(button2);
                panel->add(button3);

                button3->setSize(2.5 * bindPosition(button1) + bindSize(button2) / 4 + tgui::Vector2f(100, 50));
                REQUIRE(button3->getSize() == tgui::Vector2f(300, 210));

                button3->setPosition(2 * bindRight(button1) + bindLeft(button2) / 4 + bindWidth(button1), 50 - bindBottom(button2) + 75 * bindTop(button2));
                REQUIRE(button3->getPosition() == tgui::Vector2f(995, 5560));
                REQUIRE(button3->getAbsolutePosition() == tgui::Vector2f(1005, 5585));

                auto button4 = std::make_shared<tgui::Button>();
                button4->setSize(200, 50);

                auto button5 = std::make_shared<tgui::Button>();
                button5->setSize(bindMax(bindWidth(button4), bindHeight(button4)), bindMin(bindWidth(button4), bindHeight(button4)));
                REQUIRE(button5->getSize() == tgui::Vector2f(200, 50));

                button4->setSize(80, 120);
                REQUIRE(button5->getSize() == tgui::Vector2f(120, 80));
            }

            SECTION("Container inner size")
            {
                auto panel = tgui::Panel::create({400, 300});

                auto width = bindInnerWidth(panel);
                auto height = bindInnerHeight(panel);
                auto size = bindInnerSize(panel);

                REQUIRE(width.getValue() == 400);
                REQUIRE(height.getValue() == 300);
                REQUIRE(size.getValue() == tgui::Vector2f(400, 300));

                panel->getRenderer()->setBorders({10, 20, 15, 25});

                REQUIRE(width.getValue() == 375);
                REQUIRE(height.getValue() == 255);
                REQUIRE(size.getValue() == tgui::Vector2f(375, 255));
            }

            SECTION("Gui")
            {
                GuiNull gui;
                gui.setAbsoluteView({0, 0, 20, 15});

                auto width = bindWidth(gui);
                auto height = bindHeight(gui);
                auto size = bindSize(gui);

                REQUIRE(width.getValue() == 20);
                REQUIRE(height.getValue() == 15);
                REQUIRE(size.getValue() == tgui::Vector2f(20, 15));

                gui.setAbsoluteView({4, 3, 40, 30});

                REQUIRE(width.getValue() == 40);
                REQUIRE(height.getValue() == 30);
                REQUIRE(size.getValue() == tgui::Vector2f(40, 30));
            }

            SECTION("Bound widgets to string")
            {
                auto root = tgui::Panel::create({400, 300});

                auto panel = tgui::Panel::create({300, 200});
                root->add(panel, "Panel");

                auto button1 = std::make_shared<tgui::Button>();
                auto button2 = std::make_shared<tgui::Button>();
                auto button3 = std::make_shared<tgui::Button>();
                panel->add(button1, "b1");
                panel->add(button2, "b2");

                auto layout = bindRight(button1) + bindBottom(button2) + bindInnerWidth(panel) + bindInnerHeight(panel) + 10;
                REQUIRE(layout.toString() == "((((b1.left + b1.width) + (b2.top + b2.height)) + Panel.innerwidth) + Panel.innerheight) + 10");
            }
        }
    }

    SECTION("with strings")
    {
        SECTION("percentages")
        {
            Layout layout{"100%"};
            REQUIRE(layout.toString() == "100%");

            Layout layout2{"50%"};
            REQUIRE(layout2.toString() == "50%");

            REQUIRE(!layout.isConstant());

            auto button = std::make_shared<tgui::Button>();
            button->setPosition({"40%", "30%"});
            button->setSize({"20%", "10%"});

            REQUIRE(button->getSize() == tgui::Vector2f(0, 0));
            REQUIRE(button->getPosition() == tgui::Vector2f(0, 0));

            auto panel = std::make_shared<tgui::Panel>();
            panel->setSize(400, 300);
            panel->add(button);

            REQUIRE(button->getSize() == tgui::Vector2f(80, 30));
            REQUIRE(button->getPosition() == tgui::Vector2f(160, 90));

            SECTION("Inner size is used")
            {
                panel->getRenderer()->setBorders({5, 10, 15, 20});
                panel->getRenderer()->setPadding({25, 30, 35, 40});

                REQUIRE(button->getSize() == tgui::Vector2f(0.2f * (400 - 5 - 15 - 25 - 35), 0.1f * (300 - 10 - 20 - 30 - 40)));
                REQUIRE(button->getPosition() == tgui::Vector2f(0.4f * (400 - 5 - 15 - 25 - 35), 0.3f * (300 - 10 - 20 - 30 - 40)));
            }
        }

        SECTION("operators")
        {
            REQUIRE(Layout("2").getValue() == 2);
            REQUIRE(Layout("+2").getValue() == 2);
            REQUIRE(Layout("-2").getValue() == -2);
            REQUIRE(Layout("(2)").getValue() == 2);
            REQUIRE(Layout("-(-2)").getValue() == 2);
            REQUIRE(Layout("+(+2)").getValue() == 2);
            REQUIRE(Layout("2 + 3").getValue() == 5);
            REQUIRE(Layout("2 - 3").getValue() == -1);
            REQUIRE(Layout("2 + -3").getValue() == -1);
            REQUIRE(Layout("2 * 3").getValue() == 6);
            REQUIRE(Layout("5 / 3").getValue() == 5.f/3.f);
            REQUIRE(Layout("min(1,2)").getValue() == 1);
            REQUIRE(Layout("min(2,1)").getValue() == 1);
            REQUIRE(Layout("max(1,2)").getValue() == 2);
            REQUIRE(Layout("max(2,1)").getValue() == 2);

            REQUIRE(Layout("2").toString() == "2");
            REQUIRE(Layout("+2").toString() == "2");
            REQUIRE(Layout("-2").toString() == "-2");
            REQUIRE(Layout("(2)").toString() == "2");
            REQUIRE(Layout("-(-2)").toString() == "2");
            REQUIRE(Layout("+(+2)").toString() == "2");
            REQUIRE(Layout("2 + 3").toString() == "2 + 3");
            REQUIRE(Layout("2 - 3").toString() == "2 - 3");
            REQUIRE(Layout("2 + -3").toString() == "(2 + 0) - 3");
            REQUIRE(Layout("2 * 3").toString() == "2 * 3");
            REQUIRE(Layout("5 / 3").toString() == "5 / 3");
            REQUIRE(Layout("min(1,2)").toString() == "min(1, 2)");
            REQUIRE(Layout("max(2,1)").toString() == "max(2, 1)");

            REQUIRE(!Layout("2 + 3").isConstant());
            REQUIRE(!Layout("2 - 3").isConstant());
            REQUIRE(!Layout("2 + -3").isConstant());
            REQUIRE(!Layout("2 * 3").isConstant());
            REQUIRE(!Layout("5 / 3").isConstant());
            REQUIRE(!Layout("min(1,2)").isConstant());
            REQUIRE(!Layout("max(2,1)").isConstant());

            REQUIRE(Layout("5 + 3 * 2 - 1").getValue() == 10);
            REQUIRE(Layout("5 + 3 * (2 - 1)").getValue() == 8);
            REQUIRE(Layout("(5 + 3) * 2 - 1").getValue() == 15);
            REQUIRE(Layout("min(5 + min(2, 3), max(2, 1) * 3)").getValue() == 6);
            REQUIRE(Layout("min(5 + min(2, 3), max(2, 1) * 4)").getValue() == 7);
            REQUIRE(Layout("6 * (max(2,1) + (1 - (5))) / (-3)").getValue() == 4);

            REQUIRE(Layout("5 + 3 * 2 - 1").toString() == "(5 + (3 * 2)) - 1");
            REQUIRE(Layout("5 + 3 * (2 - 1)").toString() == "5 + (3 * (2 - 1))");
            REQUIRE(Layout("(5 + 3) * 2 - 1").toString() == "((5 + 3) * 2) - 1");
            REQUIRE(Layout("min(5 + min(2, 3), max(2, 1) * 3)").toString() == "min(5 + min(2, 3), max(2, 1) * 3)");
            REQUIRE(Layout("6 * (max(2,1) + (1 - (5))) / (-3)").toString() == "(6 * (max(2, 1) + (1 - 5))) / -3");

            REQUIRE(Layout("(5 + 3) * 2 - 1").toString() == "((5 + 3) * 2) - 1");

            REQUIRE(Layout("width").getValue() == 0);

            std::streambuf *oldbuf = std::cerr.rdbuf(0);
            REQUIRE(Layout("xyz").getValue() == 0);
            std::cerr.rdbuf(oldbuf);
        }

        SECTION("bind functions")
        {
            auto panel = std::make_shared<tgui::Panel>();
            panel->setSize(200, 180);
            panel->setPosition(10, 25);

            auto button1 = std::make_shared<tgui::Button>();
            button1->setSize(300, 50);
            button1->setPosition(40, 60);
            panel->add(button1, "b1");

            auto button2 = std::make_shared<tgui::Button>();
            button2->setPosition("b1.position");
            button2->setSize("b1.size");
            REQUIRE(button2->getSize() == tgui::Vector2f(0, 0));
            REQUIRE(button2->getPosition() == tgui::Vector2f(0, 0));
            REQUIRE(button2->getSizeLayout().toString() == "(b1.size, b1.size)");
            REQUIRE(button2->getPositionLayout().toString() == "(b1.position, b1.position)");

            panel->add(button2, "b2");
            REQUIRE(button2->getSize() == tgui::Vector2f(300, 50));
            REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));
            REQUIRE(button2->getSizeLayout().toString() == "(b1.size, b1.size)");
            REQUIRE(button2->getPositionLayout().toString() == "(b1.position, b1.position)");

            std::streambuf *oldbuf = std::cerr.rdbuf(0);
            button2->setPosition({"b1.p"});
            REQUIRE(button2->getPosition() == tgui::Vector2f(0, 0));
            std::cerr.rdbuf(oldbuf);

            button2->setPosition({"b1.pos"});
            REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));
            REQUIRE(button2->getPositionLayout().toString() == "(b1.pos, b1.pos)");

            button2->setPosition("b1.left", tgui::String{"b1.top"});
            REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));
            REQUIRE(button2->getPositionLayout().toString() == "(b1.left, b1.top)");

            button2->setPosition({"b1.x"}, {"b1.y"});
            REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));
            REQUIRE(button2->getPositionLayout().toString() == "(b1.x, b1.y)");

            button2->setSize({{"b1.right"}, {"b1.bottom"}});
            REQUIRE(button2->getSize() == tgui::Vector2f(340, 110));
            REQUIRE(button2->getSizeLayout().toString() == "(b1.left + b1.width, b1.top + b1.height)");

            oldbuf = std::cerr.rdbuf(0);
            button2->setSize({"{@, #}"});
            REQUIRE(button2->getSize() == tgui::Vector2f(0, 0));
            std::cerr.rdbuf(oldbuf);

            button2->setPosition({"parent.x"}, {"&.y"});
            REQUIRE(button2->getPosition() == tgui::Vector2f(10, 25));
            REQUIRE(button2->getPositionLayout().toString() == "(parent.x, &.y)");

            button2->setPosition({"parent.b1.parent.b1.x"}, {"&.b1.&.b1.y"});
            REQUIRE(button2->getPosition() == tgui::Vector2f(40, 60));
            REQUIRE(button2->getPositionLayout().toString() == "(parent.b1.parent.b1.x, &.b1.&.b1.y)");

            button2->setPosition({"b1.position"});
            button2->setSize({"b1.size"});
            button1->setSize(400, 40);
            button1->setPosition(60, 75);
            REQUIRE(button1->getSizeLayout().toString() == "(400, 40)");
            REQUIRE(button1->getPositionLayout().toString() == "(60, 75)");
            REQUIRE(button2->getSize() == tgui::Vector2f(400, 40));
            REQUIRE(button2->getPosition() == tgui::Vector2f(60, 75));

            auto button3 = std::make_shared<tgui::Button>();
            button1->setSize(300, 50);
            button1->setPosition(40, 60);
            button2->setSize(400, 40);
            button2->setPosition(60, 75);
            panel->add(button3);

            button3->setSize({"2.5 * b1.pos + b2.size / 4 + 50"});
            REQUIRE(button3->getSize() == tgui::Vector2f(250, 210));
            REQUIRE(button3->getSizeLayout().toString() == "(((2.5 * b1.pos) + (b2.size / 4)) + 50, ((2.5 * b1.pos) + (b2.size / 4)) + 50)");

            button3->setPosition({"2 * b1.right + b2.x / 4 + b1.w"}, {"50 - b2.bottom + 75 * b2.y"});
            REQUIRE(button3->getPosition() == tgui::Vector2f(995, 5560));
            REQUIRE(button3->getAbsolutePosition() == tgui::Vector2f(1005, 5585));
            REQUIRE(button3->getPositionLayout().toString() == "(((2 * (b1.left + b1.width)) + (b2.x / 4)) + b1.w, (50 - (b2.top + b2.height)) + (75 * b2.y))");
        }

        SECTION("No ambiguity with 0")
        {
            auto widget = std::make_shared<tgui::ClickableWidget>();
            widget->setPosition({0, 0});
            widget->setPosition(0,0);
            widget->setPosition({"0","0"});
            widget->setPosition({"0"},{"0"});
            widget->setPosition("0","0");
        }
    }

    SECTION("Bug Fixes")
    {
        SECTION("Setting negative size and reverting back to positive (https://github.com/texus/TGUI/issues/54)")
        {
            tgui::Panel::Ptr panel = std::make_shared<tgui::Panel>();
            tgui::Button::Ptr button = std::make_shared<tgui::Button>();
            panel->add(button);

            // Button width becomes -10
            panel->setSize(10, 10);
            button->setSize({"&.w - 20", "&.h"});
            REQUIRE(button->getSize() == tgui::Vector2f(-10, 10));

            // Button width will become positive again
            panel->setSize(200, 100);
            REQUIRE(button->getSize() == tgui::Vector2f(180, 100));
        }
    }
}
