/*
 * $Header: /home/harald/repos/remotetea.sf.net/remotetea/src/org/acplt/oncrpc/apps/jrpcgen/JrpcgenEnum.java,v 1.1 2003/08/13 12:03:45 haraldalbrecht Exp $
 *
 * Copyright (c) 1999, 2000
 * Lehrstuhl fuer Prozessleittechnik (PLT), RWTH Aachen
 * D-52064 Aachen, Germany.
 * All rights reserved.
 *
 * 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 program (see the file LICENSE.txt for more
 * details); if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package org.acplt.oncrpc.apps.jrpcgen;

import java.io.IOException;
import java.util.Iterator;

/**
 * The <code>JrpcgenEnum</code> class represents a single enumeration
 * from an rpcgen "x"-file. It is a "container" for the elements (constants)
 * belonging to this enumeration.
 *
 * @version $Revision: 1.1 $ $Date: 2003/08/13 12:03:45 $ $State: Exp $ $Locker:  $
 * @author Harald Albrecht
 */
public class JrpcgenEnum extends JrpcgenComplexType {

	public static class Element extends JrpcgenConst {
	
		public Element(JrpcgenContext context, String identifier, String value) {
			super(context, identifier, value);
		}
		
		@Override
		public void writeDeclaration(JrpcgenJavaFile javaFile) {
			if (javaEnumerationsEnabled()) {
	        	/*
	        	 * Write the documentation of the enumeration element.
	        	 */
	        	writeDocumentation(javaFile);
	        	
	        	/*
	        	 * Begin with the declaration.
	        	 */
	        	javaFile.newLine().beginLine().append(getIdentifier())
	        		.append('(').append(getValueAsRValue(getEnclosure())).append(')');
			} else {
				/*
				 * Enumerations are declared as constants. Therefore
				 * the request is passed to the base implementation.
				 */
				super.writeDeclaration(javaFile);
			}
		}
		
		@Override
		public String getAsRValue(String forEnclosure) {
			String asRValue = super.getAsRValue(forEnclosure);
			
			if (javaEnumerationsEnabled())
				return asRValue.concat(".getEncoding()");
			
			return asRValue;
		}
		
		@Override
		public String getAsCaseValue(String forEnclosure) {
			/*
			 * References to an enumeration value within case arms
			 * must not be precdeded by their enclosure.
			 * However: Are Java enumerations are enabled?
			 */
			if (javaEnumerationsEnabled()) {
				/*
				 * Within case arms an enumeration value is
				 * referenced by its identifier, only.
				 */
				return getIdentifier();
			} else {
				/*
				 * Java enumerations are disabled. This enumeration
				 * element is backed as an integer value within an
				 * interface definition. Therefore it is handled like
				 * a usual constant.
				 */
				return super.getAsCaseValue(forEnclosure);
			}
		}
		
		private boolean javaEnumerationsEnabled() {
			return getContext().options().javaEnumerationsEnabled();
		}
	}
	
    /**
     * Constructs a <code>JrpcgenEnum</code> and sets the identifier and all
     * its enumeration elements.
     *
     * @param context The context the new enumeration belongs to.
     * @param identifier Identifier to be declared.
     * @param enums Vector of enumeration elements of class {@link JrpcgenConst}.
     */
    public JrpcgenEnum(JrpcgenContext context, String identifier, JrpcgenConst.Table elements) {
    	super(identifier, Type.ENUM);
        /*
         * Before taking the elements, the elements get
         * their enclosure for the Java world.
         */
        Iterator<JrpcgenConst> elementIterator = elements.iterator();
        
        while (elementIterator.hasNext()) {
        	elementIterator.next().setEnclosure(identifier);
        }

        this.context = context;
        this.elements = elements;
        this.javaName = context.options().javaEnumerationsEnabled() ?
        		getIdentifier()
        		: JrpcgenBaseType.INT.getJavaName();
        this.xdrClass = context.options().javaEnumerationsEnabled() ?
        		"XdrEnum<" + getIdentifier() + ">"
        		: JrpcgenBaseType.INT.getXdrClass();
    }

    final public JrpcgenConst.Table getElements() {
    	return elements;
    }
    
    @Override
    public void generateJavaFile() {
        //
        // Create new source code file containing a Java interface representing
        // the XDR enumeration.
        //
    	try (JrpcgenJavaFile javaFile = JrpcgenJavaFile.open(getIdentifier(), context)) {
    		
    		if (context.options().noEnum) {
    			writeInterface(context, javaFile);
    		} else {
        		writeEnumeration(context, javaFile);
    		}
    		
    		
    	} catch(IOException ioException) {
    		/*
    		 * The IOexception is thrown by the close()-method
    		 * of the Java source file only.
    		 */
    		System.err.println("Cannot close source code file: "
    				+ ioException.getLocalizedMessage());
    	}

    }
    
    @Override
    public void writeXdrVectorCodingMethods(JrpcgenJavaFile javaFile, JrpcgenContext context) {
    	/*
    	 * XDR vector coding methods will only be written,
    	 * if Java enumerations are enabled. Otherwise the
    	 * implementations of the base type 'int' will be
    	 * applied.
    	 */
    	if (context.options().javaEnumerationsEnabled()) {
    		super.writeXdrVectorCodingMethods(javaFile, context);
    	}
    }
    
    @Override
    public String getJavaName() {
    	return javaName;
    }
    
    @Override
    public String getJavaClass() {
    	if (context.options().javaEnumerationsEnabled()) {
    		return javaName;
    	} else {
    		return JrpcgenBaseType.INT.getJavaClass();
    	}
    }
    
    @Override
    public String getXdrClass() {
    	return xdrClass;
    }
    
    @Override
    public void writeXdrConstructorCall(JrpcgenJavaFile javaFile, String constructorParameter) {
    	super.writeXdrConstructorCall(javaFile,
    			constructorParameter == null ?
    					elements.getFirstItem().getForEnclosure(null)
    					: constructorParameter);
    }

    @Override
    public void writeJavaToXdr(JrpcgenJavaFile javaFile, String variable) {
    	writeXdrConstructorCall(javaFile, variable);
    }
    
    @Override
    public void writeJavaToXdr(JrpcgenJavaFile javaFile, JrpcgenJavaFile.Expression expression) {
    	writeXdrConstructorCall(javaFile, expression);
    }
    
    @Override
    public void writeXdrToJava(JrpcgenJavaFile javaFile, String result) {
    	if (context.options().javaEnumerationsEnabled()) {
    		javaFile.append(result).append(".getValue()");
    	} else {
    		JrpcgenBaseType.INT.writeXdrToJava(javaFile, result);
    	}
    }
    
    @Override
    public void writeXdrEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, String variable) {
    	if (context.options().javaEnumerationsEnabled()) {
    		JrpcgenBaseType.INT.writeXdrEncodingCall(javaFile, xdrStream, variable.concat(".getEncoding()"));
    	} else {
    		JrpcgenBaseType.INT.writeXdrEncodingCall(javaFile, xdrStream, variable);
    	}
    }
    
    @Override
    public void writeXdrFixedVectorEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, String variable, String size) {
    	if (context.options().javaEnumerationsEnabled()) {
    		super.writeXdrFixedVectorEncodingCall(javaFile, xdrStream, variable, size);
    	} else {
    		JrpcgenBaseType.INT.writeXdrFixedVectorEncodingCall(javaFile, xdrStream, variable, size);
    	}
    }
    
    @Override
    public void writeXdrDynamicVectorEncodingCall(JrpcgenJavaFile javaFile, String xdrStream, String variable) {
    	if (context.options().javaEnumerationsEnabled()) {
    		super.writeXdrDynamicVectorEncodingCall(javaFile, xdrStream, variable);
    	} else {
    		JrpcgenBaseType.INT.writeXdrDynamicVectorEncodingCall(javaFile, xdrStream, variable);
    	}
    }
    
    @Override
    public void writeXdrDecodingCall(JrpcgenJavaFile javaFile, String xdrStream) {
    	if (context.options().javaEnumerationsEnabled()) {
    		javaFile.append("XdrEnum.create(")
    			.append(elements.getFirstItem().getForEnclosure(null)).append(", ").append(xdrStream).rightParenthesis();
    	} else {
    		JrpcgenBaseType.INT.writeXdrDecodingCall(javaFile, xdrStream);
    	}
    }
    
	@Override
	public void writeXdrFixedVectorDecodingCall(JrpcgenJavaFile javaFile, String xdrStream, String size) {
		if (context.options().javaEnumerationsEnabled()) {
			super.writeXdrFixedVectorDecodingCall(javaFile, xdrStream, size);
		} else {
			JrpcgenBaseType.INT.writeXdrFixedVectorDecodingCall(javaFile, xdrStream, size);
		}
	}
	
	@Override
	public void writeXdrDynamicVectorDecodingCall(JrpcgenJavaFile javaFile, String xdrStream) {
		if (context.options().javaEnumerationsEnabled()) {
			super.writeXdrDynamicVectorDecodingCall(javaFile, xdrStream);
		} else {
			JrpcgenBaseType.INT.writeXdrDynamicVectorDecodingCall(javaFile, xdrStream);
		}
	}

    @Override
    public void writeEqualsExpression(JrpcgenJavaFile javaFile, String variableLeft, String variableRight, boolean negate) {
    	if (context.options().javaEnumerationsEnabled()) {
    		super.writeEqualsExpression(javaFile, variableLeft, variableRight, negate);
    	} else {
    		JrpcgenBaseType.INT.writeEqualsExpression(javaFile, variableLeft, variableRight, negate);
    	}
    }
    
    /**
     * Returns the fully qualified identifier.
     *
     * return fully qualified identifier.
     */
    public String toString() {
        return dump(new StringBuilder()).toString();
    }


    /**
     * Dumps the enumeration together with its elements to
     * <code>System.out</code>.
     */
    public void dump() {
    	dump(System.out).println();
    }
    
    public <T extends Appendable> T dump(T appendable) {
    	try {
        	appendable.append("ENUM ").append(getIdentifier()).append(JrpcgenContext.newline());
        	
        	for (JrpcgenConst element : elements.getItemList()) {
        		element.dump(appendable.append("  ")).append(JrpcgenContext.newline());
        	}
    	} catch (IOException ioException) {
    		// Will be ignored at this place.
    	}
    	
    	return appendable;
    }
    
    private void writeEnumeration(JrpcgenContext context, JrpcgenJavaFile javaFile) {
        //TODO generate imports
    	/*
    	 * Dependent on the use of this type in vector contexts,
    	 * teh headr file is written.
    	 */
    	if (context.typesInDynamicVectorUse().contains(getIdentifier())
    			|| context.typesInFixedVectorUse().contains(getIdentifier())) {
    		/*
    		 * The imports provided by the header writing methods are needed
    		 * with respect to the generation of the vector coding methods.
    		 */
    		javaFile.writeHeader(true);
    	} else {
    		/*
    		 * No imports provided by the header writing method are needed.
    		 */
    		javaFile.writeHeader(false);
    	}

        javaFile.beginLine().println("import org.acplt.oncrpc.HasEncoding;");
//        out.println("import java.io.IOException;");
//        out.println();
//        out.println("import org.acplt.oncrpc.OncRpcException;");
//        out.println("import org.acplt.oncrpc.XdrDecodingStream;");
//        out.println("import org.acplt.oncrpc.XdrEncodingStream;");

        //
        // Spit out some description for javadoc & friends...
        //
        writeDocumentation(javaFile.newLine());

        javaFile.newLine().beginTypedefinition("public enum ").append(getIdentifier())
        	.append(" implements HasEncoding<").append(getIdentifier()).println("> {");

        for ( JrpcgenConst element : elements.getItemList() ) {
            //
            // In case an element depends on a global constant, then
            // this constant will automatically be duplicated as part
            // of this enumeration.
            //
        	element.writeDeclaration(javaFile);
        	
        	if (element != elements.getLastItem()) {
        		javaFile.append(',').newLine();
        	} else {
        		javaFile.append(';').newLine();
        	}
        }
        
        javaFile.newLine().beginLine().println("private final int encoding;");
        
        // generate ctor
        javaFile.newLine().beginPrivateConstructor(getIdentifier()).parameter("int", "encoding").endSignature();
        javaFile.beginLine().println("this.encoding = encoding;");
        javaFile.endMethod();
        
        // implement HasEncoding
        javaFile.newLine().beginPublicMethod().resultType("int").name("getEncoding").endSignature();
        javaFile.beginLine().println("return encoding;");
        javaFile.endMethod();
        
        javaFile.newLine().beginPublicMethod().resultType("HasEncoding<", getIdentifier(), ">[]")
        	.name("getValues").endSignature();
        javaFile.beginLine().println("return values();");
        javaFile.endMethod();
        
        writeXdrVectorCodingMethods(javaFile, context);
        
        //
        // Close class...
        //
        javaFile.newLine().endTypedefinition();
    }
    
    private void writeInterface(JrpcgenContext context, JrpcgenJavaFile javaFile) {
		javaFile.writeHeader(false);

        //
        // Spit out some description for javadoc & friends...
        //
    	writeDocumentation(javaFile.newLine());

        javaFile.newLine().beginTypedefinition("public interface ").append(getIdentifier()).println(" {");

        for ( JrpcgenConst element : elements.getItemList() ) {
            //
            // In case an element depends on a global constant, then
            // this constant will automatically be duplicated as part
            // of this enumeration.
            //
            element.writeDeclaration(javaFile);
        }
        
        //
        // Close class...
        //
        javaFile.endTypedefinition();
    }

    /**
     * A reference to the context the enumeration belongs to.
     */
    private final JrpcgenContext context;
    
    /**
     * Contains enumeration elements as well as their values. The elements
     * are of class {@link JrpcgenConst}.
     */
    private final JrpcgenConst.Table elements;

    private final String javaName;
    private final String xdrClass;

}

// End of JrpcgenEnum.java
