package mindustry.ui.dialogs;

import arc.*;
import arc.files.*;
import arc.func.*;
import arc.graphics.g2d.*;
import arc.input.*;
import arc.scene.event.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.pooling.*;
import mindustry.gen.*;
import mindustry.ui.*;

import java.util.*;

public class FileChooserDialog extends BaseDialog{
    private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
    static Fi lastDirectory = Core.files.absolute(Core.settings.getString("lastDirectory", homeDirectory.absolutePath()));

    Fi directory;
    private Table files;
    private ScrollPane pane;
    private TextField navigation, filefield;
    private TextButton ok;
    private FileHistory stack = new FileHistory();
    private Boolf<Fi> filter;
    private Cons<Fi> selectListener;
    private boolean open;

    public FileChooserDialog(String title, Boolf<Fi> filter, boolean open, Cons<Fi> result){
        super(title);
        setFillParent(true);
        this.open = open;
        this.filter = filter;
        this.selectListener = result;

        directory = getLastDirectory();

        onResize(() -> {
            cont.clear();
            setupWidgets();
        });

        shown(() -> {
            cont.clear();
            setupWidgets();
        });

        keyDown(KeyCode.enter, () -> {
            ok.fireClick();
        });

        addCloseListener();
    }

    private void setupWidgets(){
        cont.margin(-10);

        Table content = new Table();

        filefield = new TextField();
        filefield.setOnlyFontChars(false);
        if(!open) filefield.addInputDialog();
        filefield.setDisabled(open);

        ok = new TextButton(open ? "@load" : "@save");

        ok.clicked(() -> {
            if(ok.isDisabled()) return;
            if(selectListener != null)
                selectListener.get(directory.child(filefield.getText()));
            hide();
        });

        filefield.changed(() -> {
            ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
        });

        filefield.change();

        TextButton cancel = new TextButton("@cancel");
        cancel.clicked(this::hide);

        navigation = new TextField("");
        navigation.touchable = Touchable.disabled;

        files = new Table();
        files.marginRight(10);
        files.marginLeft(3);

        pane = new ScrollPane(files);
        pane.setOverscroll(false, false);
        pane.setFadeScrollBars(false);

        updateFiles(true);

        Table icontable = new Table();

        ImageButton up = new ImageButton(Icon.upOpen);
        up.clicked(() -> {
            directory = directory.parent();
            updateFiles(true);
        });


        ImageButton back = new ImageButton(Icon.left);
        ImageButton forward = new ImageButton(Icon.right);

        forward.clicked(() -> stack.forward());
        back.clicked(() -> stack.back());
        forward.setDisabled(() -> !stack.canForward());
        back.setDisabled(() -> !stack.canBack());

        ImageButton home = new ImageButton(Icon.home);
        home.clicked(() -> {
            directory = homeDirectory;
            setLastDirectory(directory);
            updateFiles(true);
        });

        icontable.defaults().height(60).growX().padTop(5).uniform();
        icontable.add(home);
        icontable.add(back);
        icontable.add(forward);
        icontable.add(up);

        Table fieldcontent = new Table();
        fieldcontent.bottom().left().add(new Label("@filename"));
        fieldcontent.add(filefield).height(40f).fillX().expandX().padLeft(10f);

        Table buttons = new Table();
        buttons.defaults().growX().height(60);
        buttons.add(cancel);
        buttons.add(ok);

        content.top().left();
        content.add(icontable).expandX().fillX();
        content.row();

        content.center().add(pane).colspan(3).grow();
        content.row();

        if(!open){
            content.bottom().left().add(fieldcontent).colspan(3).grow().padTop(-2).padBottom(2);
            content.row();
        }

        content.add(buttons).growX();

        cont.add(content).grow();
    }

    private void updateFileFieldStatus(){
        if(!open){
            ok.setDisabled(filefield.getText().replace(" ", "").isEmpty());
        }else{
            ok.setDisabled(!directory.child(filefield.getText()).exists() || directory.child(filefield.getText()).isDirectory());
        }
    }

    private Fi[] getFileNames(){
        Fi[] handles = directory.list(file -> !file.getName().startsWith("."));

        Arrays.sort(handles, (a, b) -> {
            if(a.isDirectory() && !b.isDirectory()) return -1;
            if(!a.isDirectory() && b.isDirectory()) return 1;
            return String.CASE_INSENSITIVE_ORDER.compare(a.name(), b.name());
        });
        return handles;
    }

    void updateFiles(boolean push){
        if(push) stack.push(directory);
        navigation.setText(directory.toString());

        GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);

        layout.setText(Fonts.def, navigation.getText());

        if(layout.width < navigation.getWidth()){
            navigation.setCursorPosition(0);
        }else{
            navigation.setCursorPosition(navigation.getText().length());
        }

        Pools.free(layout);

        files.clearChildren();
        files.top().left();
        Fi[] names = getFileNames();

        Image upimage = new Image(Icon.upOpen);
        TextButton upbutton = new TextButton(".." + directory.toString(), Styles.flatTogglet);
        upbutton.clicked(() -> {
            directory = directory.parent();
            setLastDirectory(directory);
            updateFiles(true);
        });

        upbutton.left().add(upimage).padRight(4f).padLeft(4);
        upbutton.getLabel().setAlignment(Align.left);
        upbutton.getCells().reverse();

        files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2);
        files.row();

        ButtonGroup<TextButton> group = new ButtonGroup<>();
        group.setMinCheckCount(0);

        for(Fi file : names){
            if(!file.isDirectory() && !filter.get(file)) continue; //skip non-filtered files

            String filename = file.name();

            TextButton button = new TextButton(filename.replace("[", "[["), Styles.flatTogglet);
            button.getLabel().setWrap(false);
            button.getLabel().setEllipsis(true);
            group.add(button);

            button.clicked(() -> {
                if(!file.isDirectory()){
                    filefield.setText(filename);
                    updateFileFieldStatus();
                }else{
                    directory = directory.child(filename);
                    setLastDirectory(directory);
                    updateFiles(true);
                }
            });

            filefield.changed(() -> {
                button.setChecked(filename.equals(filefield.getText()));
            });

            Image image = new Image(file.isDirectory() ? Icon.folder : Icon.fileText);

            button.add(image).padRight(4f).padLeft(4);
            button.getCells().reverse();
            files.top().left().add(button).align(Align.topLeft).fillX().expandX()
            .height(50).pad(2).padTop(0).padBottom(0).colspan(2);
            button.getLabel().setAlignment(Align.left);
            files.row();
        }

        pane.setScrollY(0f);
        updateFileFieldStatus();

        if(open) filefield.clearText();
    }

    public static synchronized void setLastDirectory(Fi directory){
        lastDirectory = directory;
        Core.settings.put("lastDirectory", directory.absolutePath());
    }

    public static synchronized Fi getLastDirectory(){
        if(!lastDirectory.exists()){
            lastDirectory = homeDirectory;
        }
        return lastDirectory;
    }

    public class FileHistory{
        private Seq<Fi> history = new Seq<>();
        private int index;

        public FileHistory(){

        }

        public void push(Fi file){
            if(index != history.size) history.truncate(index);
            history.add(file);
            index++;
        }

        public void back(){
            if(!canBack()) return;
            index--;
            directory = history.get(index - 1);
            setLastDirectory(directory);
            updateFiles(false);
        }

        public void forward(){
            if(!canForward()) return;
            directory = history.get(index);
            setLastDirectory(directory);
            index++;
            updateFiles(false);
        }

        public boolean canForward(){
            return !(index >= history.size);
        }

        public boolean canBack(){
            return !(index == 1) && index > 0;
        }
    }
}
