/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2000  Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package jacorb.idl;

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

/**
 * @author Gerald Brose
 * @version $Id: EnumType.java,v 1.11 2000/03/24 21:49:39 brose Exp $
 */

class EnumType 
    extends TypeDeclaration 
    implements SwitchTypeSpec 
{
    public SymbolList enumlist;
    int const_counter = 0;    
    private boolean written = false;

    public EnumType(int num)
    {
	super(num);
	pack_name = "";
    }

    public Object clone()
    {
	EnumType et = new EnumType( new_num());
	et.enumlist = this.enumlist;
	et.pack_name = this.pack_name;
	et.name = this.name;
	et.included = this.included;
	return et;
    }

    public TypeDeclaration declaration()
    {
	return this;
    };


    public int size()
    {
	return enumlist.v.size();
    }

    public void set_included(boolean i)
    {
	included = i;
    }

    public String typeName()
    {
	if( pack_name.length() > 0 )
	{
	    String s = ScopedName.unPseudoName( pack_name+"." + name );
	    if(! s.startsWith("org.omg") )
	    {	
		return omg_package_prefix + s;
	    }
	    else
		return s;		           
	}
	else
	    return ScopedName.unPseudoName( name ) ;
    }

    public String signature()
    {
	return  "L" +  typeName() + ";";
    }


    public boolean basic()
    {
        return true;
    }

    public void setPackage( String s, boolean propagate_into_included )
    {
        s = parser.pack_replace(s);
        if( pack_name.length() > 0 )
            pack_name = new String( s + "." + pack_name );
        else
            pack_name = s;
    }

    public void setEnclosingSymbol( IdlSymbol s )
    {
	if( enclosing_symbol != null && enclosing_symbol != s )
	    throw new RuntimeException("Compiler Error: trying to reassign container for " + name );
	enclosing_symbol = s;
    }

    public void parse() 
    {
        try
	{
	    ConstrTypeSpec ctspec = new ConstrTypeSpec( new_num() );
	    ctspec.c_type_spec = this;
	    NameTable.define( full_name(), "type" );
	    TypeMap.typedef( full_name(), ctspec );
	    String enum_ident = null;

	    // we have to get the scoping right: enums do not
	    // define scopes, but their element identifiers are scoped.
	    // for the Java mapping, we need to get the enum type name
	    // back as it defines the class name where the constants
	    // are defined. Therefore, an additional mapping in 
	    // ScopedName is required.

	    String prefix = (pack_name.length() > 0 ? pack_name + "." : "");

	    for( Enumeration e = enumlist.v.elements(); e.hasMoreElements();)
	    {
		enum_ident = (String)e.nextElement();
		NameTable.define( prefix + enum_ident, "enum label" );
		ScopedName.enumMap( prefix + enum_ident, full_name() + "." +  enum_ident );
	    }
	} 
	catch (NameAlreadyDefined p )
	{
            parser.error("Enum " + full_name() + " already defined", token );
	}
    }

    public String className()
    {
	String fullName = typeName();
	String cName;
	if( fullName.indexOf('.') > 0 )
	{
	    pack_name = fullName.substring( 0, fullName.lastIndexOf('.'));
	    cName = fullName.substring( fullName.lastIndexOf('.') + 1 );
	} 
	else 
	{
	    pack_name = "";
	    cName  = fullName;
	}
	return cName;

    }
    public String printReadExpression(String streamname)
    {
	return  toString()+ "Helper.read(" + streamname +")" ;
    }

    public String printWriteStatement(String var_name, String streamname)
    {
	return toString() + "Helper.write("+streamname+ "," +var_name+");";
    }

    public String holderName()
    {
	return typeName() + "Holder";
    }

    public String getTypeCodeExpression()
    {
	StringBuffer sb = new StringBuffer();
	sb.append("org.omg.CORBA.ORB.init().create_enum_tc(\"" + id() + "\",\"" + className()+ "\",");
	sb.append("new String[]{");

	for( Enumeration e = enumlist.v.elements(); e.hasMoreElements();)
	{
	    sb.append("\"" + (String)e.nextElement() + "\"");
	    if( e.hasMoreElements() )
		sb.append(",");
	}
	sb.append("})");
	return  sb.toString(); 
    }

    private void printHolderClass(String className, PrintWriter ps)
    {
	if( !pack_name.equals(""))
	    ps.println("package " + pack_name + ";" );

	ps.println("final public class " + className + "Holder");
	ps.println("\timplements org.omg.CORBA.portable.Streamable");
	ps.println("{");

	ps.println("\tpublic " + className + " value;\n");

	ps.println("\tpublic " + className + "Holder ()");
	ps.println("\t{");
	ps.println("\t}");

	ps.println("\tpublic " + className + "Holder (" + className + " initial)");
	ps.println("\t{");
	ps.println("\t\tvalue = initial;");
	ps.println("\t}");

	ps.println("\tpublic org.omg.CORBA.TypeCode _type()");
	ps.println("\t{");
	ps.println("\t\treturn " + className + "Helper.type();");
	ps.println("\t}");

	ps.println("\tpublic void _read(org.omg.CORBA.portable.InputStream in)");
	ps.println("\t{");
	ps.println("\t\tvalue = " + className + "Helper.read(in);");
	ps.println("\t}");

	ps.println("\tpublic void _write(org.omg.CORBA.portable.OutputStream out)");
	ps.println("\t{");
	ps.println("\t\t" + className + "Helper.write(out,value);");
	ps.println("\t}");

	ps.println("}");
    }

    private void printHelperClass(String className, PrintWriter ps)
    {
	if( !pack_name.equals(""))
	    ps.println("package " + pack_name + ";" );

	ps.println("public class " + className + "Helper");
	ps.println("{");

	ps.println("\tprivate static org.omg.CORBA.TypeCode _type = "+getTypeCodeExpression()+";");

	ps.println("\tpublic " + className + "Helper ()");
	ps.println("\t{");
	ps.println("\t}");

	String type = typeName();
	TypeSpec.printHelperClassMethods(className, ps, type);
	printIdMethod(ps);

	ps.println("\tpublic static " + className + " read(org.omg.CORBA.portable.InputStream in)");
	ps.println("\t{");
	ps.println("\t\treturn " + className + ".from_int( in.read_long());");
	ps.println("\t}");

	ps.println("\tpublic static void write(org.omg.CORBA.portable.OutputStream out, " + className + " s)");
	ps.println("\t{");
	ps.println("\t\tout.write_long(s.value());");
	ps.println("\t}");
	ps.println("}");
    }

    /** print the class that maps the enum */

    private void printEnumClass(String className, PrintWriter pw)
    {
	if( !pack_name.equals(""))
	    pw.println("package " + pack_name + ";" );

	pw.println("public final class " + className);
	pw.println("\timplements org.omg.CORBA.portable.IDLEntity\n{");

	pw.println("\tprivate int value = -1;");

	for( Enumeration e = enumlist.v.elements(); e.hasMoreElements();)
	{
	    String label = (String)e.nextElement();
	    pw.println("\tpublic static final int _" + label + " = "+ (const_counter++) + ";");
	    pw.println("\tpublic static final " + name + " " + label + " = new "+name+"(_"+label+");");
	}
	pw.println("\tpublic int value()");
	pw.println("\t{");
	pw.println("\t\treturn value;");
	pw.println("\t}");

	pw.println("\tpublic static " + name + " from_int(int value)");
	pw.println("\t{");
	pw.println("\t\tswitch (value) {");

	for( Enumeration e = enumlist.v.elements(); e.hasMoreElements();)
	{
	    String label = (String)e.nextElement();
	    pw.println("\t\t\tcase _" + label + ": return "+label+";");
	}
	pw.println("\t\t\tdefault: throw new org.omg.CORBA.BAD_PARAM();");
	pw.println("\t\t}");
	pw.println("\t}");

	pw.println("\tprotected " + name + "(int i)");
	pw.println("\t{");
	pw.println("\t\tvalue = i;");
	pw.println("\t}");
	
	pw.println("}");
    }


    /** generate required classes */

    public void print(PrintWriter ps)
    {
	/** no code generation for included definitions */
	if( included && !generateIncluded() )
	    return;

	/** only write once */

	if( written )
	    return;

	try
	{
	    String className = className();
	
	    String path = parser.out_dir + fileSeparator + pack_name.replace('.', fileSeparator );
	    File dir = new File( path );
	    if( !dir.exists() )
	    {
		if( !dir.mkdirs())
		{
		    System.err.println("Unable to create " + path );
		    System.exit(1);
		}
	    }
	
	    /** print the mapped java class */

	    String fname = className + ".java";
	    PrintWriter decl_ps = new PrintWriter(new java.io.FileWriter(new File(dir,fname)));
	    printEnumClass( className, decl_ps );
	    decl_ps.close();

	    /** print the holder class */

	    fname = className + "Holder.java";
	    decl_ps = new PrintWriter(new java.io.FileWriter(new File(dir,fname)));
	    printHolderClass( className, decl_ps );
	    decl_ps.close();

	    /** print the helper class */

	    fname = className + "Helper.java";
	    decl_ps = new PrintWriter(new java.io.FileWriter(new File(dir,fname)));
	    printHelperClass( className, decl_ps );
	    decl_ps.close();

	    IRMap.enter(this);

	    written = true;
	} 
	catch ( java.io.IOException i )
	{
	    System.err.println("File IO error");
	    i.printStackTrace();
	}
    }

    public String toString()
    {
	return typeName();
    }

}





