package org.zalando.riptide.stream;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.JavaType;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Spliterator;
import java.util.function.Consumer;

final class StreamSpliterator<T> implements Spliterator<T> {

    private final JavaType type;
    private final JsonParser parser;
    private final boolean isNotStreamOfArrays;

    StreamSpliterator(final JavaType type, final JsonParser parser) {
        this.type = type;
        this.parser = parser;
        this.isNotStreamOfArrays = !type.isArrayType() && !type.isCollectionLikeType();
    }

    @Override
    public boolean tryAdvance(final Consumer<? super T> action) {
        try {
            final JsonToken token = parser.nextToken();

            if (token == null) {
                return false;
            }

            if (isNotStreamOfArrays && skipArrayTokens(token)) {
                return false;
            }

            final T value = parser.getCodec().readValue(parser, type);
            action.accept(value);
            return true;
        } catch (final IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    private boolean skipArrayTokens(final JsonToken token) throws IOException {
        switch (token) {
            case START_ARRAY:
                parser.nextToken();
                return false;

            case END_ARRAY:
                parser.nextToken();
                return true;

            default:
                return false;
        }
    }

    @Override
    public Spliterator<T> trySplit() {
        return null;
    }

    @Override
    public long estimateSize() {
        return Long.MAX_VALUE;
    }

    @Override
    public int characteristics() {
        return ORDERED | IMMUTABLE;
    }

}
