package net.minecraft.client.gui.screens.packs;

import net.minecraft.network.chat.FormattedText;
import net.minecraft.server.packs.repository.PackSource;
import net.minecraft.network.chat.Component;
import net.minecraft.server.packs.repository.PackCompatibility;
import java.util.stream.Collector;
import com.google.common.collect.ImmutableList;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.Collection;
import java.util.Collections;
import com.google.common.collect.Lists;
import java.util.function.Consumer;
import net.minecraft.client.renderer.texture.TextureManager;
import java.util.function.BiConsumer;
import java.util.List;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.server.packs.repository.Pack;

public class PackSelectionModel<T extends Pack> {
    private final PackRepository<T> repository;
    private final List<T> selected;
    private final List<T> unselected;
    private final BiConsumer<T, TextureManager> iconBinder;
    private final Runnable onListChanged;
    private final Consumer<PackRepository<T>> output;
    
    public PackSelectionModel(final Runnable runnable, final BiConsumer<T, TextureManager> biConsumer, final PackRepository<T> aar, final Consumer<PackRepository<T>> consumer) {
        this.onListChanged = runnable;
        this.iconBinder = biConsumer;
        this.repository = aar;
        Collections.reverse(this.selected = Lists.newArrayList(aar.getSelectedPacks()));
        (this.unselected = Lists.newArrayList(aar.getAvailablePacks())).removeAll(this.selected);
        this.output = consumer;
    }
    
    public Stream<Entry> getUnselected() {
        return this.unselected.stream().<Entry>map(aap -> new UnselectedPackEntry((T)aap));
    }
    
    public Stream<Entry> getSelected() {
        return this.selected.stream().<Entry>map(aap -> new SelectedPackEntry((T)aap));
    }
    
    public void commit() {
        this.repository.setSelected(Lists.<T>reverse(this.selected).stream().map(Pack::getId).collect(ImmutableList.toImmutableList()));
        this.output.accept(this.repository);
    }
    
    public void findNewPacks() {
        this.repository.reload();
        this.unselected.clear();
        this.unselected.addAll(this.repository.getAvailablePacks());
        this.unselected.removeAll(this.selected);
    }
    
    public interface Entry {
        void bindIcon(final TextureManager elw);
        
        PackCompatibility getCompatibility();
        
        Component getTitle();
        
        Component getDescription();
        
        PackSource getPackSource();
        
        default FormattedText getExtendedDescription() {
            return this.getPackSource().decorate(this.getDescription());
        }
        
        boolean isFixedPosition();
        
        boolean isRequired();
        
        void select();
        
        void unselect();
        
        void moveUp();
        
        void moveDown();
        
        boolean isSelected();
        
        default boolean canSelect() {
            return !this.isSelected();
        }
        
        default boolean canUnselect() {
            return this.isSelected() && !this.isRequired();
        }
        
        boolean canMoveUp();
        
        boolean canMoveDown();
    }
    
    abstract class EntryBase implements Entry {
        private final T pack;
        
        public EntryBase(final T aap) {
            this.pack = aap;
        }
        
        protected abstract List<T> getSelfList();
        
        protected abstract List<T> getOtherList();
        
        @Override
        public void bindIcon(final TextureManager elw) {
            PackSelectionModel.this.iconBinder.accept(this.pack, elw);
        }
        
        @Override
        public PackCompatibility getCompatibility() {
            return this.pack.getCompatibility();
        }
        
        @Override
        public Component getTitle() {
            return this.pack.getTitle();
        }
        
        @Override
        public Component getDescription() {
            return this.pack.getDescription();
        }
        
        @Override
        public PackSource getPackSource() {
            return this.pack.getPackSource();
        }
        
        @Override
        public boolean isFixedPosition() {
            return this.pack.isFixedPosition();
        }
        
        @Override
        public boolean isRequired() {
            return this.pack.isRequired();
        }
        
        protected void toggleSelection() {
            this.getSelfList().remove(this.pack);
            this.pack.getDefaultPosition().insert(this.getOtherList(), this.pack, Function.identity(), true);
            PackSelectionModel.this.onListChanged.run();
        }
        
        protected void move(final int integer) {
            final List<T> list3 = this.getSelfList();
            final int integer2 = list3.indexOf(this.pack);
            list3.remove(integer2);
            list3.add(integer2 + integer, this.pack);
            PackSelectionModel.this.onListChanged.run();
        }
        
        @Override
        public boolean canMoveUp() {
            final List<T> list2 = this.getSelfList();
            final int integer3 = list2.indexOf(this.pack);
            return integer3 > 0 && !list2.get(integer3 - 1).isFixedPosition();
        }
        
        @Override
        public void moveUp() {
            this.move(-1);
        }
        
        @Override
        public boolean canMoveDown() {
            final List<T> list2 = this.getSelfList();
            final int integer3 = list2.indexOf(this.pack);
            return integer3 >= 0 && integer3 < list2.size() - 1 && !list2.get(integer3 + 1).isFixedPosition();
        }
        
        @Override
        public void moveDown() {
            this.move(1);
        }
    }
    
    class SelectedPackEntry extends EntryBase {
        public SelectedPackEntry(final T aap) {
            super(aap);
        }
        
        @Override
        protected List<T> getSelfList() {
            return PackSelectionModel.this.selected;
        }
        
        @Override
        protected List<T> getOtherList() {
            return PackSelectionModel.this.unselected;
        }
        
        @Override
        public boolean isSelected() {
            return true;
        }
        
        @Override
        public void select() {
        }
        
        @Override
        public void unselect() {
            this.toggleSelection();
        }
    }
    
    class UnselectedPackEntry extends EntryBase {
        public UnselectedPackEntry(final T aap) {
            super(aap);
        }
        
        @Override
        protected List<T> getSelfList() {
            return PackSelectionModel.this.unselected;
        }
        
        @Override
        protected List<T> getOtherList() {
            return PackSelectionModel.this.selected;
        }
        
        @Override
        public boolean isSelected() {
            return false;
        }
        
        @Override
        public void select() {
            this.toggleSelection();
        }
        
        @Override
        public void unselect() {
        }
    }
}
