package org.gcube.application.geoportal.client.utils;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.vdurmont.semver4j.Semver;
import org.bson.Document;
import org.bson.types.ObjectId;

import java.io.IOException;
import java.io.InputStream;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;

public class Serialization {

	public static final DateTimeFormatter FULL_FORMATTER=DateTimeFormatter.ofPattern("uuuuMMdd_HH-mm-ss");

	public static ObjectMapper mapper;

	static {
		mapper=new ObjectMapper();
		mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
		mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
		mapper.registerModule(new JavaTimeModule());

		SimpleModule s=new SimpleModule();
		s.addDeserializer(ObjectId.class,new ObjectIdDeserializer());
		s.addSerializer(ObjectId.class,new ObjectIdSerializer());

		s.addDeserializer(Semver.class,new SemverDeserializer());
		s.addSerializer(Semver.class,new SemverSerializer());


		mapper.registerModule(s);
	}

	public static <T> T read(String jsonString,Class<T> clazz) throws JsonProcessingException, IOException {
		return mapper.readerFor(clazz).readValue(jsonString);
	}

	public static <T> Iterator<T> readCollection(String jsonString, Class<T> clazz) throws IOException {
		return mapper.readerFor(clazz).readValues(jsonString);
	}


	public static <T> Iterator<T> readCollection(InputStream is, Class<T> clazz) throws IOException {
		return mapper.readerFor(clazz).readValues(is);
	}

	public static String write(Object toWrite) throws JsonProcessingException {
		String toReturn= mapper.writeValueAsString(toWrite);
		return toReturn;
	}

	//**** PROFILED DOCUMENTS

	public static final <T> T convert(Object d,Class<T> clazz){
		return mapper.convertValue(d,clazz);
	}

	public static final Document asDocument(Object obj) throws JsonProcessingException {
		return Document.parse(mapper.writeValueAsString(obj));
	}


	// ***** Serialization Exceptions

	// OBJECT ID
	private static class ObjectIdSerializer extends JsonSerializer<ObjectId> {
		@Override
		public void serialize(ObjectId objectId, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
			if (objectId == null) jsonGenerator.writeNull();
			else jsonGenerator.writeString(objectId.toString());
		}

		@Override
		public Class<ObjectId> handledType() {
			return ObjectId.class;
		}
	}
	private static class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {

		@Override
		public ObjectId deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
			String value=jsonParser.getValueAsString();
			if(value==null || value.isEmpty() || value.equals("null"))
				return null;
			else return new ObjectId(value);
		}
		@Override
		public Class<ObjectId> handledType() {
			return ObjectId.class;
		}
	}

	//Sem Version
	private static class SemverSerializer extends JsonSerializer<Semver> {
		@Override
		public void serialize(Semver semver, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
			if (semver == null) jsonGenerator.writeNull();
			else jsonGenerator.writeString(semver.getValue());
		}

		@Override
		public Class<Semver> handledType() {
			return Semver.class;
		}
	}
	private static class SemverDeserializer extends JsonDeserializer<Semver> {

		@Override
		public Semver deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
			String value=jsonParser.getValueAsString();
			if(value==null || value.isEmpty() || value.equals("null"))
				return null;
			else return new Semver(value);
		}
		@Override
		public Class<Semver> handledType() {
			return Semver.class;
		}
	}
}
