package com.replaymod.pathing.gui;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import com.replaymod.core.ReplayMod;
import com.replaymod.core.utils.Utils;
import com.replaymod.replay.ReplayModReplay;
import com.replaymod.replaystudio.pathing.PathingRegistry;
import com.replaymod.replaystudio.pathing.path.Path;
import com.replaymod.replaystudio.pathing.path.Timeline;
import com.replaymod.replaystudio.replay.ReplayFile;
import de.johni0702.minecraft.gui.GuiRenderer;
import de.johni0702.minecraft.gui.RenderInfo;
import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer;
import de.johni0702.minecraft.gui.container.GuiContainer;
import de.johni0702.minecraft.gui.container.GuiPanel;
import de.johni0702.minecraft.gui.container.GuiScreen;
import de.johni0702.minecraft.gui.container.GuiVerticalList;
import de.johni0702.minecraft.gui.element.GuiButton;
import de.johni0702.minecraft.gui.element.GuiLabel;
import de.johni0702.minecraft.gui.element.GuiTextField;
import de.johni0702.minecraft.gui.element.IGuiButton;
import de.johni0702.minecraft.gui.function.Closeable;
import de.johni0702.minecraft.gui.layout.CustomLayout;
import de.johni0702.minecraft.gui.layout.HorizontalLayout;
import de.johni0702.minecraft.gui.layout.VerticalLayout;
import de.johni0702.minecraft.gui.popup.GuiYesNoPopup;
import de.johni0702.minecraft.gui.utils.Colors;
import de.johni0702.minecraft.gui.utils.Consumer;
import org.lwjgl.util.Dimension;
import org.lwjgl.util.ReadableDimension;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Gui for loading and saving {@link Timeline Timelines}.
 */
public class GuiKeyframeRepository extends GuiScreen implements Closeable {
    public final GuiPanel contentPanel = new GuiPanel(this).setBackgroundColor(Colors.DARK_TRANSPARENT);
    public final GuiLabel title = new GuiLabel(contentPanel).setI18nText("replaymod.gui.keyframerepository.title");
    public final GuiVerticalList list = new GuiVerticalList(contentPanel).setDrawShadow(true).setDrawSlider(true);
    public final GuiPanel buttonPanel = new GuiPanel(contentPanel).setLayout(new HorizontalLayout().setSpacing(5));
    public final GuiButton overwriteButton = new GuiButton(buttonPanel).onClick(new Runnable() {
        @Override
        public void run() {
            GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this,
                    new GuiLabel().setI18nText("replaymod.gui.keyframerepo.overwrite").setColor(Colors.BLACK)
            ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no");
            Utils.addCallback(popup.getFuture(), doIt -> {
                if (doIt) {
                    timelines.put(selectedEntry.name, currentTimeline);
                    overwriteButton.setDisabled();
                    save();
                }
            }, Throwable::printStackTrace);
        }
    }).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled();
    public final GuiButton saveAsButton = new GuiButton(buttonPanel).onClick(new Runnable() {
        @Override
        public void run() {
            final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true);
            final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this,
                    new GuiLabel().setI18nText("replaymod.gui.saveas").setColor(Colors.BLACK),
                    nameField
            ).setYesI18nLabel("replaymod.gui.save").setNoI18nLabel("replaymod.gui.cancel");
            popup.getYesButton().setDisabled();
            ((VerticalLayout) popup.getInfo().getLayout()).setSpacing(7);
            nameField.onEnter(new Runnable() {
                @Override
                public void run() {
                    if (popup.getYesButton().isEnabled()) {
                        popup.getYesButton().onClick();
                    }
                }
            }).onTextChanged(new Consumer<String>() {
                @Override
                public void consume(String obj) {
                    popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
                            && !timelines.containsKey(nameField.getText()));
                }
            });
            Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(Boolean save) {
                    if (save) {
                        String name = nameField.getText();
                        timelines.put(name, currentTimeline);
                        list.getListPanel().addElements(null, new Entry(name));
                        save();
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    t.printStackTrace();
                }
            });
        }
    }).setSize(75, 20).setI18nLabel("replaymod.gui.saveas");
    public final GuiButton loadButton = new GuiButton(buttonPanel).onClick(new Runnable() {
        @Override
        public void run() {
            getMinecraft().displayGuiScreen(null);
            try {
                Timeline timeline = timelines.get(selectedEntry.name);
                for (Path path : timeline.getPaths()) {
                    path.updateAll();
                }
                future.set(timeline);
            } catch (Throwable t) {
                future.setException(t);
            }
        }
    }).setSize(75, 20).setI18nLabel("replaymod.gui.load").setDisabled();
    public final GuiButton renameButton = new GuiButton(buttonPanel).onClick(new Runnable() {
        @Override
        public void run() {
            final GuiTextField nameField = new GuiTextField().setSize(200, 20).setFocused(true).setText(selectedEntry.name);
            final GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this,
                    new GuiLabel().setI18nText("replaymod.gui.rename").setColor(Colors.BLACK),
                    nameField
            ).setYesI18nLabel("replaymod.gui.done").setNoI18nLabel("replaymod.gui.cancel");
            popup.getYesButton().setDisabled();
            ((VerticalLayout) popup.getInfo().getLayout()).setSpacing(7);
            nameField.onEnter(new Runnable() {
                @Override
                public void run() {
                    if (popup.getYesButton().isEnabled()) {
                        popup.getYesButton().onClick();
                    }
                }
            }).onTextChanged(new Consumer<String>() {
                @Override
                public void consume(String obj) {
                    popup.getYesButton().setEnabled(!nameField.getText().isEmpty()
                            && !timelines.containsKey(nameField.getText()));
                }
            });
            Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(Boolean save) {
                    if (save) {
                        String name = nameField.getText();
                        timelines.put(name, timelines.remove(selectedEntry.name));
                        selectedEntry.name = name;
                        selectedEntry.label.setText(name);
                        save();
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    t.printStackTrace();
                }
            });
        }
    }).setSize(75, 20).setI18nLabel("replaymod.gui.rename").setDisabled();
    public final GuiButton removeButton = new GuiButton(buttonPanel).onClick(new Runnable() {
        @Override
        public void run() {
            GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this,
                    new GuiLabel().setI18nText("replaymod.gui.keyframerepo.delete").setColor(Colors.BLACK)
            ).setYesI18nLabel("replaymod.gui.delete").setNoI18nLabel("replaymod.gui.cancel");
            Futures.addCallback(popup.getFuture(), new FutureCallback<Boolean>() {
                @Override
                public void onSuccess(Boolean delete) {
                    if (delete) {
                        timelines.remove(selectedEntry.name);
                        list.getListPanel().removeElement(selectedEntry);

                        selectedEntry = null;
                        overwriteButton.setDisabled();
                        loadButton.setDisabled();
                        renameButton.setDisabled();
                        removeButton.setDisabled();
                        save();
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    t.printStackTrace();
                }
            });
        }
    }).setSize(75, 20).setI18nLabel("replaymod.gui.remove").setDisabled();

    private final Map<String, Timeline> timelines = new LinkedHashMap<>();
    private final Timeline currentTimeline;
    private final SettableFuture<Timeline> future = SettableFuture.create();
    private final PathingRegistry registry;
    private final ReplayFile replayFile;

    private Entry selectedEntry;

    {
        setBackground(Background.NONE);
        setLayout(new CustomLayout<GuiScreen>() {
            @Override
            protected void layout(GuiScreen container, int width, int height) {
                pos(contentPanel, width / 2 - width(contentPanel) / 2, height / 2 - height(contentPanel) / 2);
            }
        });
        contentPanel.setLayout(new CustomLayout<GuiPanel>() {
            @Override
            protected void layout(GuiPanel container, int width, int height) {
                pos(title, width / 2 - width(title) / 2, 5);
                size(list, width, height - 10 - height(buttonPanel) - 10 - y(title) - height(title) - 5);
                pos(list, width / 2 - width(list) / 2, y(title) + height(title) + 5);
                pos(buttonPanel, width / 2 - width(buttonPanel) / 2, y(list) + height(list) + 10);
            }

            @Override
            public ReadableDimension calcMinSize(GuiContainer<?> container) {
                ReadableDimension screenSize = getMinSize();
                return new Dimension(screenSize.getWidth() - 10, screenSize.getHeight() - 10);
            }
        });
    }

    public GuiKeyframeRepository(PathingRegistry registry, ReplayFile replayFile, Timeline currentTimeline) throws IOException {
        this.registry = registry;
        this.replayFile = replayFile;
        this.currentTimeline = currentTimeline;

        timelines.putAll(replayFile.getTimelines(registry));

        for (Map.Entry<String, Timeline> entry : timelines.entrySet()) {
            list.getListPanel().addElements(null, new Entry(entry.getKey()));
        }
    }

    @Override
    public void display() {
        super.display();
        ReplayModReplay.instance.getReplayHandler().getOverlay().setVisible(false);
    }

    @Override
    public void close() {
        ReplayModReplay.instance.getReplayHandler().getOverlay().setVisible(true);
    }

    public SettableFuture<Timeline> getFuture() {
        return future;
    }

    public void save() {
        try {
            replayFile.writeTimelines(registry, timelines);
        } catch (IOException e) {
            e.printStackTrace();
            ReplayMod.instance.printWarningToChat("Error saving timelines: " + e.getMessage());
        }
    }

    public class Entry extends AbstractGuiClickableContainer<Entry> {
        public final GuiLabel label = new GuiLabel(this);
        private String name;

        public Entry(String name) {
            this.name = name;

            setLayout(new CustomLayout<Entry>() {
                @Override
                protected void layout(Entry container, int width, int height) {
                    pos(label, 5, height / 2 - height(label) / 2);
                }

                @Override
                public ReadableDimension calcMinSize(GuiContainer<?> container) {
                    return new Dimension(buttonPanel.calcMinSize().getWidth(), 16);
                }
            });
            label.setText(name);
        }

        @Override
        protected void onClick() {
            selectedEntry = this;
            buttonPanel.forEach(IGuiButton.class).setEnabled();
        }

        @Override
        public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) {
            if (selectedEntry == this) {
                renderer.drawRect(0, 0, size.getWidth(), size.getHeight(), Colors.BLACK);
                renderer.drawRect(0, 0, 2, size.getHeight(), Colors.WHITE);
            }
            super.draw(renderer, size, renderInfo);
        }

        @Override
        protected Entry getThis() {
            return this;
        }
    }
}
