// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.component;

import java.util.function.IntPredicate;
import com.hypixel.hytale.function.consumer.IntObjectConsumer;
import java.util.function.Consumer;
import java.util.Arrays;
import com.hypixel.hytale.common.util.ArrayUtil;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;

public class ArchetypeChunk<ECS_TYPE>
{
    @Nonnull
    private static final ArchetypeChunk[] EMPTY_ARRAY;
    @Nonnull
    protected final Store<ECS_TYPE> store;
    @Nonnull
    protected final Archetype<ECS_TYPE> archetype;
    protected int entitiesSize;
    @Nonnull
    protected Ref<ECS_TYPE>[] refs;
    protected Component<ECS_TYPE>[][] components;
    
    public static <ECS_TYPE> ArchetypeChunk<ECS_TYPE>[] emptyArray() {
        return ArchetypeChunk.EMPTY_ARRAY;
    }
    
    public ArchetypeChunk(@Nonnull final Store<ECS_TYPE> store, @Nonnull final Archetype<ECS_TYPE> archetype) {
        this.refs = new Ref[16];
        this.store = store;
        this.archetype = archetype;
        this.components = new Component[archetype.length()][];
        for (int i = archetype.getMinIndex(); i < archetype.length(); ++i) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)archetype.get(i);
            if (componentType != null) {
                this.components[componentType.getIndex()] = new Component[16];
            }
        }
    }
    
    @Nonnull
    public Archetype<ECS_TYPE> getArchetype() {
        return this.archetype;
    }
    
    public int size() {
        return this.entitiesSize;
    }
    
    @Nonnull
    public Ref<ECS_TYPE> getReferenceTo(final int index) {
        if (index < 0 || index >= this.entitiesSize) {
            throw new IndexOutOfBoundsException(index);
        }
        return this.refs[index];
    }
    
    public <T extends Component<ECS_TYPE>> void setComponent(final int index, @Nonnull final ComponentType<ECS_TYPE, T> componentType, @Nonnull final T component) {
        componentType.validateRegistry(this.store.getRegistry());
        if (index < 0 || index >= this.entitiesSize) {
            throw new IndexOutOfBoundsException(index);
        }
        if (!this.archetype.contains(componentType)) {
            throw new IllegalArgumentException("Entity doesn't have component type " + String.valueOf(componentType));
        }
        this.components[componentType.getIndex()][index] = component;
    }
    
    @Nullable
    public <T extends Component<ECS_TYPE>> T getComponent(final int index, @Nonnull final ComponentType<ECS_TYPE, T> componentType) {
        componentType.validateRegistry(this.store.getRegistry());
        if (index < 0 || index >= this.entitiesSize) {
            throw new IndexOutOfBoundsException(index);
        }
        if (!this.archetype.contains(componentType)) {
            return null;
        }
        return (T)this.components[componentType.getIndex()][index];
    }
    
    public int addEntity(@Nonnull final Ref<ECS_TYPE> ref, @Nonnull final Holder<ECS_TYPE> holder) {
        if (!this.archetype.equals(holder.getArchetype())) {
            throw new IllegalArgumentException("EntityHolder is not for this archetype chunk!");
        }
        final int entityIndex = this.entitiesSize++;
        final int oldLength = this.refs.length;
        if (oldLength <= entityIndex) {
            final int newLength = ArrayUtil.grow(entityIndex);
            this.refs = Arrays.copyOf(this.refs, newLength);
            for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); ++i) {
                final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(i);
                if (componentType != null) {
                    final int componentTypeIndex = componentType.getIndex();
                    this.components[componentTypeIndex] = Arrays.copyOf(this.components[componentTypeIndex], newLength);
                }
            }
        }
        this.refs[entityIndex] = ref;
        for (int j = this.archetype.getMinIndex(); j < this.archetype.length(); ++j) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType2 = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(j);
            if (componentType2 != null) {
                this.components[componentType2.getIndex()][entityIndex] = holder.getComponent(componentType2);
            }
        }
        return entityIndex;
    }
    
    @Nonnull
    public Holder<ECS_TYPE> copyEntity(final int entityIndex, @Nonnull final Holder<ECS_TYPE> target) {
        if (entityIndex >= this.entitiesSize) {
            throw new IndexOutOfBoundsException(entityIndex);
        }
        final Component<ECS_TYPE>[] entityComponents = target.ensureComponentsSize(this.archetype.length());
        for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); ++i) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(i);
            if (componentType != null) {
                final int componentTypeIndex = componentType.getIndex();
                final Component<ECS_TYPE> component = this.components[componentTypeIndex][entityIndex];
                entityComponents[componentTypeIndex] = component.clone();
            }
        }
        target.init(this.archetype, entityComponents);
        return target;
    }
    
    @Nonnull
    public Holder<ECS_TYPE> copySerializableEntity(@Nonnull final ComponentRegistry.Data<ECS_TYPE> data, final int entityIndex, @Nonnull final Holder<ECS_TYPE> target) {
        if (entityIndex >= this.entitiesSize) {
            throw new IndexOutOfBoundsException(entityIndex);
        }
        final Archetype<ECS_TYPE> serializableArchetype = this.archetype.getSerializableArchetype(data);
        final Component<ECS_TYPE>[] entityComponents = target.ensureComponentsSize(serializableArchetype.length());
        for (int i = serializableArchetype.getMinIndex(); i < serializableArchetype.length(); ++i) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)serializableArchetype.get(i);
            if (componentType != null) {
                final int componentTypeIndex = componentType.getIndex();
                final Component<ECS_TYPE> component = this.components[componentTypeIndex][entityIndex];
                entityComponents[componentTypeIndex] = component.cloneSerializable();
            }
        }
        target.init(serializableArchetype, entityComponents);
        return target;
    }
    
    @Nonnull
    public Holder<ECS_TYPE> removeEntity(final int entityIndex, @Nonnull final Holder<ECS_TYPE> target) {
        if (entityIndex >= this.entitiesSize) {
            throw new IndexOutOfBoundsException(entityIndex);
        }
        final Component<ECS_TYPE>[] entityComponents = target.ensureComponentsSize(this.archetype.length());
        for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); ++i) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(i);
            if (componentType != null) {
                final int componentTypeIndex = componentType.getIndex();
                entityComponents[componentTypeIndex] = this.components[componentTypeIndex][entityIndex];
            }
        }
        final int lastIndex = this.entitiesSize - 1;
        if (entityIndex != lastIndex) {
            this.fillEmptyIndex(entityIndex, lastIndex);
        }
        this.refs[lastIndex] = null;
        for (int j = this.archetype.getMinIndex(); j < this.archetype.length(); ++j) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType2 = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(j);
            if (componentType2 != null) {
                this.components[componentType2.getIndex()][lastIndex] = null;
            }
        }
        this.entitiesSize = lastIndex;
        target.init(this.archetype, entityComponents);
        return target;
    }
    
    public void transferTo(@Nonnull final Holder<ECS_TYPE> tempInternalEntityHolder, @Nonnull final ArchetypeChunk<ECS_TYPE> chunk, @Nonnull final Consumer<Holder<ECS_TYPE>> modification, @Nonnull final IntObjectConsumer<Ref<ECS_TYPE>> referenceConsumer) {
        final Component<ECS_TYPE>[] entityComponents = new Component[this.archetype.length()];
        for (int entityIndex = 0; entityIndex < this.entitiesSize; ++entityIndex) {
            final Ref<ECS_TYPE> ref = this.refs[entityIndex];
            this.refs[entityIndex] = null;
            for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); ++i) {
                final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(i);
                if (componentType != null) {
                    final int componentTypeIndex = componentType.getIndex();
                    entityComponents[componentTypeIndex] = this.components[componentTypeIndex][entityIndex];
                    this.components[componentTypeIndex][entityIndex] = null;
                }
            }
            tempInternalEntityHolder._internal_init(this.archetype, entityComponents, this.store.getRegistry().getUnknownComponentType());
            modification.accept(tempInternalEntityHolder);
            final int newEntityIndex = chunk.addEntity(ref, tempInternalEntityHolder);
            referenceConsumer.accept(newEntityIndex, ref);
        }
        this.entitiesSize = 0;
    }
    
    public void transferSomeTo(@Nonnull final Holder<ECS_TYPE> tempInternalEntityHolder, @Nonnull final ArchetypeChunk<ECS_TYPE> chunk, @Nonnull final IntPredicate shouldTransfer, @Nonnull final Consumer<Holder<ECS_TYPE>> modification, @Nonnull final IntObjectConsumer<Ref<ECS_TYPE>> referenceConsumer) {
        int firstTransfered = Integer.MIN_VALUE;
        int lastTransfered = Integer.MIN_VALUE;
        final Component<ECS_TYPE>[] entityComponents = new Component[this.archetype.length()];
        for (int entityIndex = 0; entityIndex < this.entitiesSize; ++entityIndex) {
            if (shouldTransfer.test(entityIndex)) {
                if (firstTransfered == Integer.MIN_VALUE) {
                    firstTransfered = entityIndex;
                }
                lastTransfered = entityIndex;
                final Ref<ECS_TYPE> ref = this.refs[entityIndex];
                this.refs[entityIndex] = null;
                for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); ++i) {
                    final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(i);
                    if (componentType != null) {
                        final int componentTypeIndex = componentType.getIndex();
                        entityComponents[componentTypeIndex] = this.components[componentTypeIndex][entityIndex];
                        this.components[componentTypeIndex][entityIndex] = null;
                    }
                }
                tempInternalEntityHolder.init(this.archetype, entityComponents);
                modification.accept(tempInternalEntityHolder);
                final int newEntityIndex = chunk.addEntity(ref, tempInternalEntityHolder);
                referenceConsumer.accept(newEntityIndex, ref);
            }
        }
        if (firstTransfered != Integer.MIN_VALUE) {
            if (lastTransfered == this.entitiesSize - 1) {
                this.entitiesSize = firstTransfered;
                return;
            }
            final int newSize = this.entitiesSize - (lastTransfered - firstTransfered + 1);
            for (int entityIndex2 = firstTransfered; entityIndex2 <= lastTransfered; ++entityIndex2) {
                if (this.refs[entityIndex2] == null) {
                    final int lastIndex = this.entitiesSize - 1;
                    if (lastIndex == lastTransfered) {
                        break;
                    }
                    if (entityIndex2 != lastIndex) {
                        this.fillEmptyIndex(entityIndex2, lastIndex);
                    }
                    --this.entitiesSize;
                }
            }
            this.entitiesSize = newSize;
        }
    }
    
    protected void fillEmptyIndex(final int entityIndex, final int lastIndex) {
        final Ref<ECS_TYPE> ref = this.refs[lastIndex];
        this.store.setEntityChunkIndex(ref, entityIndex);
        for (int i = this.archetype.getMinIndex(); i < this.archetype.length(); ++i) {
            final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(i);
            if (componentType != null) {
                final Component<ECS_TYPE>[] componentArr = this.components[componentType.getIndex()];
                componentArr[entityIndex] = componentArr[lastIndex];
            }
        }
        this.refs[entityIndex] = ref;
    }
    
    public void appendDump(@Nonnull final String prefix, @Nonnull final StringBuilder sb) {
        sb.append(prefix).append("archetype=").append(this.archetype).append("\n");
        sb.append(prefix).append("entitiesSize=").append(this.entitiesSize).append("\n");
        for (int i = 0; i < this.entitiesSize; ++i) {
            sb.append(prefix).append("\t- ").append(this.refs[i]).append("\n");
            sb.append(prefix).append("\t").append("components=").append("\n");
            for (int x = this.archetype.getMinIndex(); x < this.archetype.length(); ++x) {
                final ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>> componentType = (ComponentType<ECS_TYPE, ? extends Component<ECS_TYPE>>)this.archetype.get(x);
                if (componentType != null) {
                    sb.append(prefix).append("\t\t- ").append(componentType.getIndex()).append("\t").append(this.components[componentType.getIndex()][x]).append("\n");
                }
            }
        }
    }
    
    @Nonnull
    @Override
    public String toString() {
        return "ArchetypeChunk{archetype=" + String.valueOf(this.archetype) + ", entitiesSize=" + this.entitiesSize + ", entityReferences=" + Arrays.toString(this.refs) + ", components=" + Arrays.toString(this.components);
    }
    
    static {
        EMPTY_ARRAY = new ArchetypeChunk[0];
    }
}
