// Copyright 2000, CERN, Geneva, Switzerland and SLAC, Stanford, California, U.S.A.
package org.freehep.jaco.generator;

import java.io.*;
import java.util.Vector;

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

/**
 * @author Mark Donszelmann
 * @version $Id: JavaProxy.java,v 1.1.1.1 2004/05/31 05:08:51 kuromaru Exp $
 */
public class JavaProxy extends JavaInterface
{
    public JavaProxy() {
    }

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

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

        warning();

        if (!clazz.getPackageName(packagePrefix).equals(""))
        {
            out.println("package "+clazz.getPackageName(packagePrefix) + ";");
            out.println();
        }

        // imports
        out.println("import org.freehep.jaco.*;");
        out.println();

        // comments
        String[] comments = clazz.getComments(language);
        for (int i = 0; i < comments.length; i++)
            out.println(comments[i]);
        out.println();

        // class
        out.println("public class "+clazz.getName() + "Proxy extends CPPProxy implements "+
        clazz.getName());
        out.println("{");

        // constructor
        out.println("    protected "+clazz.getName() + "Proxy (int handle)");
        out.println("    {");
        out.println("        super(handle);");
        out.println("    }");
        out.println();

        // Create sudo constructors here (create methods)

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

        // methods
        Vector allMethods = new Vector();
        out.println("// methods of interface: "+
        ((clazz.getPackageName(packagePrefix).equals("")) ? "" :
            clazz.getPackageName(packagePrefix) + ".") + clazz.getName());
        out.println();
        IMethod[] methods = clazz.getMethods();
        for (int i = 0; i < methods.length; i++)
        {
            printMethod(methods[i]);
            out.println();
            allMethods.addElement(methods[i]);
        }

        // inherited methods
        String[] interfaces = clazz.getInterfaces();
        if (interfaces.length > 0)
        {
            for (int i = 0; i < interfaces.length; i++)
            {
                // FIXME: the interface name could also be fully qualified
                String iname = clazz.getPackageName(null) + "."+interfaces[i];
                IClass iclazz = Jaco.getRTTI().getClass(iname);
                if (iclazz == null)
                {
                    // FIXME: abort should maybe be an exception
                    System.err.println("Cannot find '"+iname + "' in RTTI");
                    System.exit(1);
                }
                out.println("// methods of interface: "+
                iclazz.getPackageName(packagePrefix) + "."+
                iclazz.getName());
                IMethod[] imethods = iclazz.getMethods();
                for (int j = 0; j < imethods.length; j++)
                {
                    // Test to see if this method has already been overriden
                    if (!allMethods.contains(imethods[j]))
                    {
                        printMethod(imethods[j]);
                        out.println();
                        allMethods.addElement(imethods[j]);
                    }
                }
            }
        }
        out.println("   public void delete()");
        out.println("   {");
        out.println("      deleteNative(handle);");
        out.println("   }");
        out.println("   public void finalize()");
        out.println("   {");
        out.println("      releaseNative(handle);");
        out.println("   }");
        out.println("   private native void deleteNative(int handle);");
        out.println("   private native void releaseNative(int handle);");

        // end class
        out.println("}");
        return false;
    }

    private void printConstructor(IConstructor constructor, IClass clazz)
    {
        out.print("   public static native "+clazz.getName() + " create(");

        INamedType[] parameterTypes = constructor.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++)
        {
            if (i > 0)
                out.print(", ");
            out.print(namedType(parameterTypes[i]));
        }
        out.print(" )");

        String[] exceptionTypes = constructor.getExceptionTypes();
        if (exceptionTypes.length > 0)
        {
            out.println(" throws ");
            for (int i = 0; i < exceptionTypes.length; i++)
            {
                if (i > 0)
                    out.print(", ");
                out.println(exceptionTypes[i]);
            }
        }

        out.println(";");
    }

    private void printMethod(IMethod method)
    {
        INamedType[] parameterTypes = method.getParameterTypes();
        String[] exceptionTypes = method.getExceptionTypes();

        if (!method.isStatic())
        {
            printMethodHeader(method);
            out.println();
            out.println("   {");
            out.print("      ");
            if (!method.getReturnType().isVoid()) out.print("return ");
            out.print(method.getName() + "Native(handle");
            for (int i = 0; i < parameterTypes.length; i++)
            {
                if (parameterTypes[i].getType().isPrimitive())
                {
                    out.print(", "+parameterTypes[i].getName());
                }
                else // FIXME: What if it is not a proxy
                {
                    out.print(", ((CPPProxy) "+parameterTypes[i].getName()+").getHandle()");
                }

            }
            out.println(");");
            out.println("   }");
            out.println();
        }

        if (method.isStatic())
            out.print("   public native ");
        else
            out.print("   private native ");
        if (method.isStatic())
            out.print("static ");
        out.print(type(method.getReturnType()));
        out.print(" ");
        out.print(method.getName());
        out.print("Native (int handle");

        for (int i = 0; i < parameterTypes.length; i++)
        {
            if (parameterTypes[i].getType().isPrimitive())
            {
                out.print(", "+namedType(parameterTypes[i]));
            }
            else
            {
                out.print(", int "+parameterTypes[i].getName());
            }
        }
        out.print(" )");

        if (exceptionTypes.length > 0)
        {
            out.print(" throws ");
            for (int i = 0; i < exceptionTypes.length; i++)
            {
                out.print(exceptionTypes[i]);
                if (i < exceptionTypes.length - 1)
                    out.print(", ");
            }
        }
        out.println(";");
    }
}

