package com.simon816.chatui;

import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.simon816.chatui.lib.ChatUILib;
import com.simon816.chatui.lib.PlayerChatView;
import com.simon816.chatui.lib.PlayerContext;
import com.simon816.chatui.lib.TopWindow;
import com.simon816.chatui.tabs.Tab;
import com.simon816.chatui.tabs.TextBufferTab;
import com.simon816.chatui.ui.AnchorPaneUI;
import com.simon816.chatui.ui.LineFactory;
import com.simon816.chatui.ui.UIComponent;
import com.simon816.chatui.util.TextBuffer;
import com.simon816.chatui.util.TextUtils;
import org.spongepowered.api.text.LiteralText;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.action.TextActions;
import org.spongepowered.api.text.format.TextColors;
import org.spongepowered.api.text.format.TextStyles;

import java.util.List;

public class Window implements TopWindow {

    private final List<Tab> tabs = Lists.newArrayList();
    private final AnchorPaneUI pane;
    private int activeIndex = -1;

    public Window() {
        this.pane = new AnchorPaneUI();
        this.pane.addWithConstraint(new TabBar(), AnchorPaneUI.ANCHOR_TOP);
        this.pane.getChildren().add(new TabHolder());
        this.pane.addWithConstraint(new StatusBar(), AnchorPaneUI.ANCHOR_BOTTOM);

    }

    public void addTab(Tab tab, boolean switchTab) {
        int idx = this.tabs.indexOf(tab);
        if (idx == -1) {
            this.tabs.add(tab);
            idx = this.tabs.size() - 1;
        }
        if (switchTab) {
            setTab(idx);
        }
    }

    public void removeTab(Tab tab) {
        removeTab(this.tabs.indexOf(tab));
    }

    public void removeTab(int index) {
        if (index < this.activeIndex) {
            this.activeIndex--;
        } else if (index == this.activeIndex) {
            setTab(index - 1);
        }
        this.tabs.remove(index).onClose();
    }

    public void setTab(int tabIndex) {
        if (tabIndex >= this.tabs.size()) {
            throw new IllegalArgumentException("Tab index doesn't exist");
        }
        if (tabIndex == this.activeIndex) {
            return;
        }
        if (this.activeIndex > -1) {
            this.tabs.get(this.activeIndex).onBlur();
        }
        this.activeIndex = tabIndex;
        this.tabs.get(this.activeIndex).onFocus();
    }

    public void setTab(Tab tab) {
        setTab(this.tabs.indexOf(tab));
    }

    public Tab getActiveTab() {
        return this.tabs.get(this.activeIndex);
    }

    public int getActiveIndex() {
        return this.activeIndex;
    }

    @Override
    public void onClose() {
        for (Tab tab : this.tabs) {
            tab.onClose();
        }
        this.tabs.clear();
        this.activeIndex = -1;
    }

    @Override
    public boolean onCommand(PlayerChatView view, String[] args) {
        String cmd = args[0];
        if (cmd.equals("settab")) {
            this.setTab(Integer.parseInt(args[1]));
        } else if (cmd.equals("closetab")) {
            this.removeTab(Integer.parseInt(args[1]));
        } else if (this.activeIndex != -1) {
            return this.tabs.get(this.activeIndex).onCommand(view, args);
        } else {
            return false;
        }
        return true;
    }

    @Override
    public void onTextInput(PlayerChatView view, Text input) {
        if (this.activeIndex != -1) {
            this.tabs.get(this.activeIndex).onTextInput(view, input);
        }
    }

    @Override
    public Text draw(PlayerContext ctx) {
        return this.pane.draw(ctx);
    }

    List<Tab> getTabs() {
        return this.tabs;
    }

    static final LiteralText newTab = Text.builder("[+]")
            .color(TextColors.GREEN)
            .onClick(ChatUILib.command("newtab"))
            .onHover(TextActions.showText(Text.of("New Tab")))
            .build();

    private class TabBar implements UIComponent {

        private int tabListLines = 1;

        TabBar() {
        }

        @Override
        public void draw(PlayerContext ctx, LineFactory lineFactory) {
            int maxWidth = ctx.width;
            Text.Builder builder = Text.builder();
            int currentLineWidth = 0;
            TextBuffer buffer = new TextBuffer(ctx);
            buffer.append(TextUtils.charCache('╔'));
            this.tabListLines = 1;
            for (int i = 0; i < getTabs().size(); i++) {
                Tab tab = getTabs().get(i);
                buffer.append(createTabButton(i));
                if (tab.hasCloseButton()) {
                    buffer.append(createCloseButton(i));
                }
                buffer.append(TextUtils.charCache('═'));
                currentLineWidth = addTabElement(lineFactory, buffer, builder, currentLineWidth, maxWidth, ctx);
                buffer.clear();
            }
            buffer.append(newTab);
            currentLineWidth = addTabElement(lineFactory, buffer, builder, currentLineWidth, maxWidth, ctx);
            builder.append(ctx.utils().repeatAndTerminate('═', this.tabListLines == 1 ? '╗' : '╣', maxWidth - currentLineWidth));
            lineFactory.appendNewLine(builder.build(), ctx);
        }

        private int addTabElement(LineFactory lineFactory, TextBuffer buffer, Text.Builder builder, int currentLineWidth, int maxWidth,
                PlayerContext ctx) {
            if (currentLineWidth + buffer.getWidth() > maxWidth) {
                // Overspilled - finish this line and move to another one
                builder.append(ctx.utils().repeatAndTerminate('═', this.tabListLines == 1 ? '╗' : '╣', maxWidth - currentLineWidth));
                lineFactory.appendNewLine(builder.build(), ctx);
                char startChar = '╠';
                builder.removeAll();
                currentLineWidth = ctx.utils().getWidth(startChar, false);
                builder.append(TextUtils.charCache(startChar));
                this.tabListLines++;
            }
            currentLineWidth += buffer.getWidth();
            builder.append(buffer.getContents());
            return currentLineWidth;
        }

        private Text createTabButton(int tabIndex) {
            Text.Builder button = getTabs().get(tabIndex).getTitle().toBuilder();
            button.color(TextColors.GREEN);
            if (tabIndex == getActiveIndex()) {
                button.style(TextStyles.BOLD);
            } else {
                button.onClick(ChatUILib.command("settab " + tabIndex));
            }
            return button.build();
        }

        private Text createCloseButton(int tabIndex) {
            return Text.builder("[x]").color(TextColors.RED)
                    .onClick(ChatUILib.command("closetab " + tabIndex))
                    .onHover(TextActions.showText(Text.of("Close")))
                    .build();
        }

        @Override
        public int getPrefHeight(PlayerContext ctx) {
            return this.tabListLines;
        }

    }

    private class TabHolder implements UIComponent {

        TabHolder() {
        }

        @Override
        public void draw(PlayerContext ctx, LineFactory lineFactory) {
            if (getActiveIndex() != -1) {
                getActiveTab().draw(ctx, lineFactory);
            }
        }
    }

    private class StatusBar implements UIComponent {

        StatusBar() {
        }

        @Override
        public void draw(PlayerContext ctx, LineFactory lineFactory) {
            Text prefix = Text.of("╚Status: ");
            Text content = getStatusBarContent();
            int remWidth = ctx.width - ctx.utils().getWidth(prefix) - ctx.utils().getWidth(content);
            Text line = Text.builder().append(prefix, content, ctx.utils().repeatAndTerminate('═', '╝', remWidth)).build();
            lineFactory.appendNewLine(line, ctx);
        }

        private Text getStatusBarContent() {
            int unread = 0;
            for (Tab tab : getTabs()) {
                if (tab instanceof TextBufferTab) {
                    unread += ((TextBufferTab) tab).getUnread();
                }
            }
            return Text.builder(String.valueOf(unread)).color(TextColors.RED)
                    .append(Text.builder(" unread messages").color(TextColors.RESET).build()).build();
        }

        @Override
        public int getPrefHeight(PlayerContext ctx) {
            return 1;
        }

    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("active", getActiveTab())
                .add("tabs", this.tabs)
                .toString();
    }

}