package com.netflix.serialization;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.Type;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.ObjectWriter;
import org.codehaus.jackson.type.TypeReference;

import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;

public class JacksonCodec<T extends Object> implements Serializer<T>, Deserializer<T> {
    
    private static final JacksonCodec instance = new JacksonCodec();
    
    private final ObjectMapper mapper = new ObjectMapper();

    @Override
    public T deserialize(InputStream in, TypeDef<T> type)
            throws IOException {
        if (String.class.equals(type.getRawType())) {
            return (T) CharStreams.toString(new InputStreamReader(in, Charsets.UTF_8));
        }
        return mapper.readValue(in, new TypeTokenBasedReference<T>(type));
    }
    
    @Override
    public void serialize(OutputStream out, T object, TypeDef<?> type) throws IOException {
        if (type == null) {
            mapper.writeValue(out, object);
        } else {
            ObjectWriter writer = mapper.writerWithType(new TypeTokenBasedReference(type));
            writer.writeValue(out, object);
        }
    }
    
    public static final <T> JacksonCodec<T> getInstance() {
        return instance;
    }
}

class TypeTokenBasedReference<T> extends TypeReference<T> {
    
    final Type type;
    public TypeTokenBasedReference(TypeDef<T> typeToken) {
        type = typeToken.getType();    
        
    }

    @Override
    public Type getType() {
        return type;
    }
}