package org.freehep.jaco.generator;

import java.io.*;
import org.freehep.jaco.rtti.*;

/**
 * @author Tony Johnson
 * @version $Id: JNIStub.java,v 1.12 2002/10/07 18:16:11 duns Exp $
 */
public class JNIStub extends AbstractGenerator
{
   protected String name;

    public JNIStub() {
        super();
    }

    protected String getName(IClass clazz) {
        String name = clazz.getName() + "Proxy";
        String directory = clazz.getPackageName(packagePrefix);
        if (!directory.equals(""))
        {
         name = directory.replace('.','_') + "_"+name;
        }
      return name;
    }

   public String filename(IClass clazz)
   {
      return getName(clazz) + ".h";
   }

   public boolean print(PrintWriter out, IClass clazz)
   {
      this.out = out;

      String name = getName(clazz);
      warning();

      out.println("#include <jni.h>");
      out.println();

      out.println("#ifndef _"+name);
      out.println("#define _"+name);
      out.println("#ifdef __cplusplus");
      out.println("extern \"C\" {");
      out.println("#endif");
      out.println();

      IConstructor[] constructors = clazz.getConstructors();
      for (int i = 0; i < constructors.length; i++)
      {
         printConstructorHeader(constructors, i, false);
         out.println(";");
         out.println();
      }

      IMethod[] methods = clazz.getMethods();
      for (int i = 0; i < methods.length; i++)
      {
         if (!methods[i].isStatic())
         {
            printMethodHeader(methods, i, false);
            out.println(";");
            out.println();
         }
      }
      out.println("JNIEXPORT void JNICALL Java_"+name + "_deleteNative");
      out.println("   (JNIEnv *, jobject, jint);");
      out.println("JNIEXPORT void JNICALL Java_"+name + "_releaseNative");
      out.println("   (JNIEnv *, jobject, jint);");


      out.println("#ifdef __cplusplus");
      out.println("}");
      out.println("#endif");
      out.println("#endif");

      return false;
   }

   protected void printConstructorHeader(IConstructor[] constructors, int index,
           boolean names)
   {
      IConstructor constructor = constructors[index];
      boolean overloaded = false;
      for (int j=0; j < constructors.length; j++)
      {
         if (j == index) continue;
         if (constructor.getName().equals(constructors[j].getName())) overloaded = true;
         if (overloaded) break;
      }
      out.print("JNIEXPORT jobject JNICALL Java_"+name + "_create");
      if (overloaded) out.print("__"+parameterSignature(constructor.getParameterTypes()));
      out.println();
      if (names)
         out.print("   (JNIEnv* env, jclass cls");
      else
         out.print("   (JNIEnv*, jclass");

      printParameters(constructor.getParameterTypes(), names);
      out.print(")");
   }
   protected void printMethodHeader(IMethod[] methods, int index, boolean names)
   {
      IMethod method = methods[index];
      boolean overloaded = false;
      for (int j=0; j < methods.length; j++)
      {
         if (j == index) continue;
         if (method.getName().equals(methods[j].getName())) overloaded = true;
         if (overloaded) break;
      }
      out.print("JNIEXPORT ");
      IType returnType = method.getReturnType();
      if (returnType.isPrimitive())
         out.print(returnType.getName());
      else
         out.print("jobject");

      out.print(" JNICALL Java_"+name + "_"+method.getName() + "Native");
      if (overloaded) out.print("__I"+parameterSignature(method.getParameterTypes()));
      out.println();
      if (names)
         out.print("  (JNIEnv* env, jobject obj, jint handle");
      else
         out.print("  (JNIEnv*, jobject, jint");

      printParameters(method.getParameterTypes(), names);
      out.print(")");
   }
   private void printParameters(INamedType[] parameterTypes, boolean names)
   {
      for (int i = 0; i < parameterTypes.length; i++)
      {
         out.print(", ");
         out.print(parameterTypes[i].getType().getName());
         if (names)
            out.print(" p"+i);
      }
   }
   /**
    * Generates the method signature
    */
   private String parameterSignature(INamedType[] parameterTypes)
   {
      StringBuffer result = new StringBuffer();
      for (int i=0; i<parameterTypes.length; i++)
      {
         IType type = parameterTypes[i].getType();
         int d = type.getDimension();
         for (int j=0; j<d; j++) result.append("_3");
         if (type.isPrimitive())
         {
            String name = type.getName();
            if      (name.equals("int"))     result.append('I');
            else if (name.equals("double"))  result.append('D');
            else if (name.equals("short"))   result.append('S');
            else if (name.equals("byte"))    result.append('B');
            else if (name.equals("char"))    result.append('C');
            else if (name.equals("long"))    result.append('J');
            else if (name.equals("float"))   result.append('F');
            else if (name.equals("boolean")) result.append('Z');
            else throw new RuntimeException("Unknown primitive type "+name);
            // what else
         }
         else
         {
            String name = type.getName();
            name = name.replace('.','_');
            result.append('L');
            result.append(name);
            result.append("_2");
         }
      }
      return result.toString();
   }
}

