package p.studio.compiler.source.tables;

import p.studio.compiler.source.identifiers.SourceIdentifier;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

public abstract class DenseTable<IDENTIFIER extends SourceIdentifier, VALUE>
        implements DenseTableReader<IDENTIFIER, VALUE>, DenseTableWriter<IDENTIFIER, VALUE> {
    private final List<VALUE> values = new ArrayList<>();
    private final Function<Integer, IDENTIFIER> identifierGenerator;

    protected DenseTable(final Function<Integer, IDENTIFIER> identifierGenerator) {
        this.identifierGenerator = Objects.requireNonNull(identifierGenerator);
    }

    @Override
    public final int size() { return values.size(); }

    @Override
    public final VALUE get(final IDENTIFIER identifier) {
        return values.get(identifier.getIndex());
    }

    @Override
    public IDENTIFIER register(final VALUE value) {
        final int idx = values.size();
        values.add(value);
        return identifierGenerator.apply(idx);
    }

    @Override
    public void clear() {
        values.clear();
    }

    @Override
    public Iterable<IDENTIFIER> identifiers() {
        return () -> new Iterator<>() {

            private int current = 0;

            @Override
            public boolean hasNext() {
                return current < values.size();
            }

            @Override
            public IDENTIFIER next() {
                return identifierGenerator.apply(current++);
            }
        };
    }

    @Override
    public Iterable<VALUE> values() {
        return () -> new Iterator<>() {

            private int current = 0;

            @Override
            public boolean hasNext() {
                return current < values.size();
            }

            @Override
            public VALUE next() {
                final var identifier = identifierGenerator.apply(current++);
                return get(identifier);
            }
        };
    }

    @Override
    public Iterator<IDENTIFIER> iterator() {
        return identifiers().iterator();
    }
}
