package com.jenjinstudios.io.serialization;

import com.google.gson.*;
import com.jenjinstudios.io.Message;
import com.jenjinstudios.io.annotations.MessageAdapter;
import org.reflections.Reflections;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Used to deserialize Message objects with Gson.
 *
 * @author Caleb Brinkman
 */
public class GsonMessageDeserializer implements JsonDeserializer<Message>
{
    private final Map<String, Class> annotatedMessages;

    /**
     * Construct a new GsonMessageDeserializer.
     */
    public GsonMessageDeserializer() {
        annotatedMessages = new HashMap<>(10);

        Reflections reflections = new Reflections("");
        final Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(MessageAdapter.class);
        for (Class annotatedClass : annotated) {
            Annotation annotation = annotatedClass.getAnnotation(MessageAdapter.class);
            String messageClass = ((MessageAdapter) annotation).adaptFrom().getName();
            annotatedMessages.put(messageClass, annotatedClass);
        }
    }

    @Override
    public Message deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
          throws JsonParseException
    {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonElement classElement = jsonObject.get("class");
        if (classElement == null) {
            throw new JsonParseException("Message must specify class to be deserialized into");
        }
        String className = classElement.getAsString();
        Class<Message> lookupClass;
        if (annotatedMessages.containsKey(className)) {
            lookupClass = annotatedMessages.get(className);
        } else {
            try {
                lookupClass = (Class<Message>) Class.forName(className);
            } catch (ClassNotFoundException | ClassCastException e) {
                throw new JsonParseException("Message implementation not found or incorrect: ", e);
            }
        }
        JsonElement fieldsElement = jsonObject.get("fields");
        return (fieldsElement != null)
              ? context.deserialize(fieldsElement, lookupClass)
              : context.deserialize(new JsonObject(), lookupClass);
    }
}

@@ -511,16 +511,23 @@
 private 
+static 
 final Ma
@@ -547,156 +547,23 @@
 ss%3E 
-annotatedMessages;%0A%0A    /**%0A     * Construct a new GsonMessageDeserializer.%0A     */%0A    public GsonMessageDeserializer() %7B%0A        annotatedMessages
+MESSAGE_CLASSES
  = n
@@ -582,23 +582,36 @@
 (10);%0A%0A 
+   static %7B%0A
        
+ 
 Reflecti
@@ -678,20 +678,41 @@
 ss%3C?
-%3E%3E annotated
+ extends Message%3E%3E messageClasses
  = r
@@ -729,34 +729,26 @@
 .get
+Sub
 Types
-AnnotatedWith
+Of
 (Message
 Adap
@@ -735,39 +735,32 @@
 bTypesOf(Message
-Adapter
 .class);%0A       
@@ -775,34 +775,37 @@
 ass 
-annotatedClass : annotated
+messageClass : messageClasses
 ) %7B%0A
@@ -840,25 +840,23 @@
 ation = 
-annotated
+message
 Class.ge
@@ -906,22 +906,66 @@
     
-String message
+if (annotation != null) %7B%0A                String adaptFrom
 Clas
@@ -1037,60 +1037,172 @@
     
-annotatedMessages.put(messageClass, annotatedClass);
+    MESSAGE_CLASSES.put(adaptFromClass, messageClass);%0A            %7D else %7B%0A                MESSAGE_CLASSES.put(messageClass.getName(), messageClass);%0A            %7D
 %0A   
@@ -1721,427 +1721,42 @@
 lass
-;%0A        if (annotatedMessages.containsKey(className)) %7B%0A            lookupClass = annotatedMessages.get(className);%0A        %7D else %7B%0A            try %7B%0A                lookupClass = (Class%3CMessage%3E) Class.forName(className);%0A            %7D catch (ClassNotFoundException %7C ClassCastException e) %7B%0A                throw new JsonParseException(%22Message implementation not found or incorrect: %22, e);%0A            %7D%0A        %7D
+ = MESSAGE_CLASSES.get(className);
 %0A   

Improve reflection performance