/*
 * Copyright 2008 Google Inc.
 *
 * 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.
 */
package com.google.gwt.dev;

import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JConstructor;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.TypeOracle;

import java.io.PrintStream;

@Deprecated
class SignatureDumper {

  public interface Filter {
    boolean shouldPrint(JAbstractMethod method);

    boolean shouldPrint(JClassType type);

    boolean shouldPrint(JField field);
  }

  public static void dumpSignatures(TypeOracle typeOracle, PrintStream out,
      Filter filter) {
    out.println("# Contains all signatures dumped from the GWT compiler");
    out.println("FileVersion 1");
    out.println("GwtVersion " + About.getGwtVersionNum());
    out.print(dumpAllSignatures(typeOracle, filter));
    out.close();
  }

  private static void addMethods(JAbstractMethod[] methods,
      StringBuilder result, Filter filter) {
    for (JAbstractMethod currentMeth : methods) {
      if (!filter.shouldPrint(currentMeth)) {
        continue;
      }
      if (currentMeth.isConstructor() != null) {
        result.append(" method <init>");
      } else if (currentMeth.isMethod() != null) {
        result.append(" method ");
        if (currentMeth.isMethod().isStatic()) {
          result.append("static ");
        }
        result.append(currentMeth.getName());
      } else {
        continue;
      }
      result.append(" (");
      for (JParameter currentParam : currentMeth.getParameters()) {
        result.append(currentParam.getType().getJNISignature());
      }
      result.append(')');
      if (currentMeth.isConstructor() != null) {
        result.append('V');
      } else {
        result.append(((JMethod) currentMeth).getReturnType().getJNISignature());
      }
      result.append('\n');
    }
  }

  private static void addFields(JField[] fields, StringBuilder result, Filter filter) {
    for (JField currentField : fields) {
      if (!filter.shouldPrint(currentField)) {
        continue;
      }
      result.append(" field ");
      if (currentField.isStatic()) {
        result.append("static ");
      }
      result.append(currentField.getName());
      result.append(' ');
      result.append(currentField.getType().getJNISignature());
      result.append('\n');
    }
  }

  /**
   * Dumps the signatures within this typeOracle. Singatures may appear multiple
   * times.
   */
  private static String dumpAllSignatures(TypeOracle typeOracle, Filter filter) {
    StringBuilder result = new StringBuilder();
    for (JClassType current : typeOracle.getTypes()) {
      if (!filter.shouldPrint(current)) {
        continue;
      }
      if (current.isInterface() != null) {
        result.append("interface ");
      } else {
        result.append("class ");
      }
      result.append(current.getJNISignature());
      JClassType superclass = current.getSuperclass();
      if (superclass != null) {
        result.append(" extends ");
        result.append(superclass.getJNISignature());
      }
      for (JClassType currentInterface : current.getImplementedInterfaces()) {
        result.append(" implements ");
        result.append(currentInterface.getJNISignature());
      }
      result.append('\n');

      result.append(" method static <clinit> ()V\n");
      JConstructor[] constructors = current.getConstructors();
      if (constructors.length == 0) {
        result.append(" method <init> ()V\n");
      } else {
        addMethods(constructors, result, filter);
      }
      addMethods(current.getMethods(), result, filter);

      addFields(current.getFields(), result, filter);

      // If the parent class was filtered out, we might be missing some public API in the output
      // as they will be inherited by the current class. So let's add those as well:
      for (JClassType c = superclass; c != null && !filter.shouldPrint(c); c = c.getSuperclass()) {
        addMethods(c.getMethods(), result, filter);
        addFields(c.getFields(), result, filter);
      }
    }
    return result.toString();
  }

}
