/**
 * This file is part of Eclipse Steady.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 * SPDX-FileCopyrightText: Copyright (c) 2018-2020 SAP SE or an SAP affiliate company and Eclipse Steady contributors
 */
package org.eclipse.steady.java.sign.gson;

import java.lang.reflect.Type;
import java.util.Set;

import org.eclipse.steady.ConstructId;
import org.eclipse.steady.java.JavaClassId;
import org.eclipse.steady.java.JavaClassInit;
import org.eclipse.steady.java.JavaConstructorId;
import org.eclipse.steady.java.JavaId;
import org.eclipse.steady.java.JavaMethodId;
import org.eclipse.steady.java.JavaPackageId;
import org.eclipse.steady.java.sign.ASTConstructBodySignature;
import org.eclipse.steady.java.sign.ASTSignatureChange;

import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

/**
 * <p>GsonHelper class.</p>
 */
public class GsonHelper {

  /**
   * Returns a Vulas-specific GsonBuilder, i.e., with several custom serializers and deserializers registered.	 *
   *
   * @return a {@link com.google.gson.GsonBuilder} object.
   */
  public static GsonBuilder getCustomGsonBuilder() {
    final GsonBuilder gson = new GsonBuilder();

    // Subclasses of ConstructId and JavaId
    gson.registerTypeAdapter(ConstructId.class, new ConstructIdSerializer());
    gson.registerTypeAdapter(JavaPackageId.class, new ConstructIdSerializer());
    gson.registerTypeAdapter(JavaClassId.class, new ConstructIdSerializer());
    gson.registerTypeAdapter(JavaClassInit.class, new ConstructIdSerializer());
    gson.registerTypeAdapter(JavaConstructorId.class, new ConstructIdSerializer());
    gson.registerTypeAdapter(JavaMethodId.class, new ConstructIdSerializer());

    // Signature-related classes
    gson.registerTypeAdapter(ASTSignatureChange.class, new ASTSignatureChangeDeserializer());
    gson.registerTypeAdapter(ASTConstructBodySignature.class, new ASTSignatureDeserializer());

    return gson;
  }

  static class ConstructIdSerializer
      implements JsonSerializer<ConstructId>, JsonDeserializer<ConstructId> {

    public JsonElement serialize(
        ConstructId src, Type typeOfSrc, JsonSerializationContext context) {
      final JsonObject c = new JsonObject();
      c.addProperty("lang", src.getLanguage().toString());
      c.addProperty("type", JavaId.typeToString(((JavaId) src).getType()));
      c.addProperty("qname", src.getQualifiedName());
      final Set<String> annotations = ((JavaId) src).getAnnotations();
      if (!annotations.isEmpty()) {
        final JsonArray anno = new JsonArray();
        for (String a : annotations) {
          anno.add(new JsonPrimitive(a));
        }
        c.add("a", anno);
      }
      return c;
    }

    public ConstructId deserialize(
        JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
      // To be returned
      ConstructId cid = null;

      final JsonObject c = (JsonObject) json;
      final String t = c.getAsJsonPrimitive("type").getAsString();
      final String qn = c.getAsJsonPrimitive("qname").getAsString();

      if (JavaId.typeToString(JavaId.Type.PACKAGE).equals(t)) cid = new JavaPackageId(qn);
      else if (JavaId.typeToString(JavaId.Type.CLASS).equals(t)) cid = JavaId.parseClassQName(qn);
      else if (JavaId.typeToString(JavaId.Type.CLASSINIT).equals(t))
        cid = JavaId.parseClassInitQName(qn);
      else if (JavaId.typeToString(JavaId.Type.METHOD).equals(t)) cid = JavaId.parseMethodQName(qn);
      else if (JavaId.typeToString(JavaId.Type.CONSTRUCTOR).equals(t))
        cid = JavaId.parseConstructorQName(qn);

      // TODO: Add annotations

      return cid;
    }
  }
}
