#include <Display/WidgetDisplay/ServerSelection/ServerSelection.hpp>
#include <Widget/Builder/Builder.hpp>
#include <Widget/Label/Label.hpp>
#include <Widget/Layout/Center/Center.hpp>
#include <Widget/Layout/Column/Column.hpp>
#include <Widget/Button/Button.hpp>
#include <Widget/Layout/CompoundWidget/IconText/IconText.hpp>
#include <Widget/TextField/TextField.hpp>
#include <Display/WidgetDisplay/WorldDisplay/WorldDisplay.hpp>
#include <Display/WidgetDisplay/MessageDisplay/MessageDisplay.hpp>
#include <Utilities/ServerSessionTable.hpp>
#include <World/RemoteWorld/RemoteWorld.hpp>
#include <Display/WidgetDisplay/ConnectingDisplay/ConnectingDisplay.hpp>
#include <Authenticator/SessionAuthenticator/SessionAuthenticator.hpp>
#include <Display/WidgetDisplay/LoginDisplay/LoginDisplay.hpp>
#include <Display/WidgetDisplay/RegisterDisplay/RegisterDisplay.hpp>
#include <Widget/Panel/Panel.hpp>
#include <Widget/Layout/CompoundWidget/ListBox/ListBox.hpp>
#include <Widget/Layout/Row/Row.hpp>
#include <Widget/Layout/Column/HorizontalSeparator/HorizontalSeparator.hpp>
#include <Widget/Layout/Row/VerticalSeparator/VerticalSeparator.hpp>
#include <Widget/Layout/CompoundWidget/Form/Form.hpp>
#include <Widget/IconLabel/IconLabel.hpp>
#include <Pinger/Pinger.hpp>
#include <Widget/Layout/CompoundWidget/PingView/PingView.hpp>
#include <Widget/Button/BackButton/BackButton.hpp>

class SessionRow : public Row
{
private:
    Pinger pinger;

public:
    SessionRow(const ServerSession &ses) :
        Row(
            new Center(new PingView(pinger)),
            new Center(
                new Column(
                    new Label(ses.getLabel()),
                    new Builder<Pinger::State>(
                        [this]() { return pinger.getState(); },
                        [this](Pinger::State state) -> Widget*
                        {
                            if (state == Pinger::State::Pinging)
                            {
                                return new Label("Pinging...", Color::faded);
                            }
                            else if (state == Pinger::State::Error)
                            {
                                return new Label(pinger.getError(), Color::error);
                            }
                            else if (state == Pinger::State::Success)
                            {
                                Widget *subtext = new Label(pinger.getMotto(), Color::faded);
                                Version serverVersion = pinger.getServerVersion();
                                
                                if (serverVersion != Version::ofThisBuild)
                                {
                                    subtext = new Row(
                                        new Label(formatString("$+", serverVersion), Color::warning),
                                        subtext
                                    );
                                }

                                return subtext;
                            }
                            else
                            {
                                crash("Unexpected state value.");
                            }
                        }
                    )
                )
            )
        ),
        pinger(ses.server)
    {}
};

ServerSelection::ServerSelection() :
    sessions(getServerSessions()),
    lastKnownGeneration(getServerSessionsGeneration())
{
    setWidget(
        new Center(
            new Form(
                new IconText(Material::multiplayer, "Select server"),

                new Row(
                    (Widget::focused = new ListBox<ServerSession>(
                        sessions,
                        selectedSession,
                        [](ServerSession ses) -> Widget*
                        {
                            return new SessionRow(ses);
                        },
                        [](ServerSession ses)
                        {
                            std::shared_ptr<SessionAuthenticator> auth = std::make_shared<SessionAuthenticator>(ses.username, ses.sid);
                            Display::navPush<ConnectingDisplay>(auth, ses.server);
                        }
                    )),

                    new VerticalSeparator(),

                    new Column(
                        new Button("Log in", []()
                        {
                            Display::navPush<LoginDisplay>();
                        }),

                        new Panel(glm::ivec2(2, 1)),

                        new Button("Register", []()
                        {
                            Display::navPush<RegisterDisplay>();
                        })
                    )
                ),

                Layout::WithProportion<1>(),
                new Center(new BackButton())
            )
        )
    );
}

void ServerSelection::update()
{
    if (lastKnownGeneration != getServerSessionsGeneration())
    {
        lastKnownGeneration = getServerSessionsGeneration();
        sessions = getServerSessions();
    }
}