package org.xbib.datastructures.json.iterator;

import org.xbib.datastructures.json.iterator.spi.Decoder;
import org.xbib.datastructures.json.iterator.spi.JsonException;
import org.xbib.datastructures.json.iterator.spi.JsoniterSpi;
import java.io.IOException;
import java.lang.reflect.Type;

class MapKeyDecoders {

    public static Decoder registerOrGetExisting(Type mapKeyType) {
        String cacheKey = JsoniterSpi.getMapKeyDecoderCacheKey(mapKeyType);
        Decoder mapKeyDecoder = JsoniterSpi.getMapKeyDecoder(cacheKey);
        if (null != mapKeyDecoder) {
            return mapKeyDecoder;
        }
        mapKeyDecoder = createMapKeyDecoder(mapKeyType);
        JsoniterSpi.addNewMapDecoder(cacheKey, mapKeyDecoder);
        return mapKeyDecoder;
    }

    private static Decoder createMapKeyDecoder(Type mapKeyType) {
        if (String.class == mapKeyType) {
            return new StringKeyDecoder();
        }
        if (mapKeyType instanceof Class && ((Class) mapKeyType).isEnum()) {
            return new EnumKeyDecoder((Class) mapKeyType);
        }
        Decoder decoder = CodegenImplNative.NATIVE_DECODERS.get(mapKeyType);
        if (decoder != null) {
            return new NumberKeyDecoder(decoder);
        }
        throw new JsonException("can not decode map key type: " + mapKeyType);
    }

    private static class StringKeyDecoder implements Decoder {

        @Override
        public Object decode(JsonIterator iter) throws IOException {
            return iter.readString();
        }
    }

    private static class EnumKeyDecoder implements Decoder {

        private final Class enumClass;

        private EnumKeyDecoder(Class enumClass) {
            this.enumClass = enumClass;
        }

        @Override
        public Object decode(JsonIterator iter) throws IOException {
            return iter.read(enumClass);
        }
    }

    private static class NumberKeyDecoder implements Decoder {

        private final Decoder decoder;

        private NumberKeyDecoder(Decoder decoder) {
            this.decoder = decoder;
        }

        @Override
        public Object decode(JsonIterator iter) throws IOException {
            if (IterImpl.nextToken(iter) != '"') {
                throw iter.reportError("decode number map key", "expect \"");
            }
            Object key = decoder.decode(iter);
            if (IterImpl.nextToken(iter) != '"') {
                throw iter.reportError("decode number map key", "expect \"");
            }
            return key;
        }
    }
}
