package mindustry.ui.dialogs;

import arc.graphics.*;
import arc.input.*;
import arc.math.*;
import arc.scene.event.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.ui.*;

import java.util.*;

import static arc.Core.*;
import static mindustry.Vars.*;

public class DatabaseDialog extends BaseDialog{
    private final OrderedMap<String, OrderedMap<String, Seq<UnlockableContent>>> sortedContents = new OrderedMap<>();
    private final OrderedMap<String, Seq<UnlockableContent>> tmpCategory = new OrderedMap<>();

    private TextField search;
    private Table all = new Table();

    private @Nullable Seq<UnlockableContent> allTabs;
    //sun means "all content"
    private UnlockableContent tab = Planets.sun;

    public DatabaseDialog(){
        super("@database");

        shouldPause = true;
        addCloseButton();
        shown(() -> {
            checkTabList();
            sortContents();
            if(state.isCampaign() && allTabs.contains(state.getPlanet())){
                tab = state.getPlanet();
            }else if(state.isGame() && state.rules.planet != null && allTabs.contains(state.rules.planet)){
                tab = state.rules.planet;
            }

            rebuild();
        });
        onResize(this::rebuild);

        all.margin(20).marginTop(0f).marginRight(30f);

        cont.top();
        cont.table(s -> {
            s.image(Icon.zoom).padRight(8);
            search = s.field(null, text -> rebuild()).growX().get();
            search.setMessageText("@players.search");
        }).fillX().padBottom(4).row();

        cont.pane(all).scrollX(false);
    }

    void checkTabList(){
        if(allTabs == null){
            Seq<Content>[] allContent = Vars.content.getContentMap();
            ObjectSet<UnlockableContent> all = new ObjectSet<>();
            for(var contents : allContent){
                for(var content : contents){
                    if(content instanceof UnlockableContent u){
                        all.addAll(u.databaseTabs);
                    }
                }
            }
            allTabs = all.toSeq().sort();
            allTabs.insert(0, Planets.sun);
        }
    }

    void sortContents(){
        Seq<Content>[] allContent = Vars.content.getContentMap();
        sortedContents.clear();
        for(var contents : allContent){
            for(var content : contents){
                if(content instanceof UnlockableContent u){
                    //some mods don't initialize these properly
                    String cat = u.databaseCategory == null ? u.getContentType().name() : u.databaseCategory;
                    String tag = u.databaseTag == null ? "default" : u.databaseTag;

                    var categoryContents = sortedContents.get(cat, new OrderedMap<>());
                    var taggedContents = categoryContents.get(tag, new Seq<>());
                    taggedContents.add(u);
                    categoryContents.put(tag, taggedContents);
                    sortedContents.put(cat, categoryContents);
                }
            }
        }
    }

    void rebuild(){
        checkTabList();

        all.clear();
        var text = search.getText().toLowerCase();

        all.table(t -> {
            int i = 0;
            for(var content : allTabs){
                t.button(content == Planets.sun ? Icon.eyeSmall : content instanceof Planet p ? Icon.icons.get(p.icon, Icon.commandRally) : new TextureRegionDrawable(content.uiIcon), Styles.clearNoneTogglei, iconMed, () -> {
                    tab = content;
                    rebuild();
                }).size(50f).checked(b -> tab == content).tooltip(content == Planets.sun ? "@all" : content.localizedName).with(but -> {
                    but.getStyle().imageUpColor = content instanceof Planet p ? p.iconColor : Color.white.cpy();
                });

                if(++i % 10 == 0) t.row();
            }
        }).row();

        boolean hasResult = false;

        for(int i = 0; i < sortedContents.size; i++){
            String categoryName = sortedContents.orderedKeys().get(i);
            OrderedMap<String, Seq<UnlockableContent>> categoryContents = sortedContents.get(categoryName);

            tmpCategory.clear();

            boolean categoryHasResult = false;

            for(int j = 0; j < categoryContents.size; j++){
                String tagName = categoryContents.orderedKeys().get(j);
                Seq<UnlockableContent> array = categoryContents.get(tagName).select(u ->
                !u.isHidden() && !u.hideDatabase &&
                (tab == Planets.sun || u.allDatabaseTabs || u.databaseTabs.contains(tab)) &&
                (text.isEmpty() || u.localizedName.toLowerCase(Locale.ROOT).contains(text))).as();
                if(array.isEmpty()) continue;

                hasResult = true;
                categoryHasResult = true;

                //sorting only makes sense when in-game; otherwise, banned blocks can't exist
                if(state.isGame()){
                    array.sort(Structs.comps(Structs.comparingBool(UnlockableContent::isBanned), Structs.comparingInt(u -> u.id)));
                }

                tmpCategory.put(tagName, array);
            }

            if(tmpCategory.isEmpty() || !categoryHasResult) continue;

            all.add("@database-category." + categoryName).growX().left().color(Pal.accent);
            all.row();
            all.image().pad(5).padLeft(0).padRight(0).height(3).color(Pal.accent).growX();
            all.row();

            all.table(sub -> {
                for(int j = 0; j < tmpCategory.size; j++){
                    String tagName = tmpCategory.orderedKeys().get(j);
                    Seq<UnlockableContent> array = tmpCategory.get(tagName);
                    if(array == null || array.isEmpty()) continue;

                    if(!"default".equals(tagName)){
                        sub.table(tag -> {
                            tag.add("@database-tag." + tagName).left().color(Pal.gray);
                            tag.image().growX().pad(5).height(3).color(Pal.gray);
                        }).pad(4, 8, 4, 8).growX();
                        sub.row();
                    }

                    sub.table(list -> {
                        list.left();

                        int cols = (int)Mathf.clamp((graphics.getWidth() - Scl.scl(30)) / Scl.scl(32 + 12), 1, 22);
                        int count = 0;

                        for(var unlock : array){
                            Image image = unlocked(unlock) ? new Image(new TextureRegionDrawable(unlock.uiIcon), mobile ? Color.white : Color.lightGray).setScaling(Scaling.fit) : new Image(Icon.lock, Pal.gray);

                            //banned cross
                            if(state.isGame() && unlock.isBanned()){
                                list.stack(image, new Image(Icon.cancel){{
                                    setColor(Color.scarlet);
                                    touchable = Touchable.disabled;
                                }}).size(8 * 4).pad(3);
                            }else if(state.isGame() && state.patcher.isPatched(unlock)){
                                list.stack(image, new Table(){{
                                    right().bottom().touchable = Touchable.disabled;
                                    // Interpolated color (lerp lightishGray and white) for better contrast
                                    image(Icon.fileSmall).size(12f).color(Tmp.c1.set(Color.white).a(0.5f));
                                }}).size(8 * 4).pad(3);
                            }else{
                                list.add(image).size(8 * 4).pad(3);
                            }

                            ClickListener listener = new ClickListener();
                            image.addListener(listener);
                            if(!mobile && unlocked(unlock)){
                                image.addListener(new HandCursorListener());
                                image.update(() -> image.color.lerp(!listener.isOver() ? Color.lightGray : Color.white, Mathf.clamp(0.4f * Time.delta)));
                            }

                            if(unlocked(unlock)){
                                image.clicked(() -> {
                                    if(input.keyDown(KeyCode.shiftLeft) && Fonts.getUnicode(unlock.name) != 0){
                                        app.setClipboardText((char)Fonts.getUnicode(unlock.name) + "");
                                        ui.showInfoFade("@copied");
                                    }else{
                                        ui.content.show(unlock);
                                    }
                                });
                                image.addListener(new Tooltip(t -> t.background(Tex.button).add(unlock.localizedName + (settings.getBool("console") ? "\n[gray]" + unlock.name : ""))));
                            }

                            if((++count) % cols == 0){
                                list.row();
                            }
                        }

                        for(int k = 0; k < cols - count; k++){
                            Image image = new Image();
                            image.setColor(Color.clear);
                            list.add(image).size(8 * 4).pad(3);
                        }
                    });
                    sub.row();
                }


            }).growX().left().padBottom(10);

            all.row();
        }

        if(!hasResult){
            all.add("@none.found");
        }
    }

    boolean unlocked(UnlockableContent content){
        return (!Vars.state.isCampaign() && !Vars.state.isMenu()) || content.unlocked();
    }
}
