/*
 * $Header: /home/harald/repos/remotetea.sf.net/remotetea/src/org/acplt/oncrpc/apps/jrpcgen/JrpcgenVersionInfo.java,v 1.1 2003/08/13 12:03:47 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.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

/**
 * The <code>JrpcgenVersionInfo</code> class contains information about a
 * specific version of an ONC/RPC program as defined in a rpcgen "x"-file.
 *
 * @version $Revision: 1.1 $ $Date: 2003/08/13 12:03:47 $ $State: Exp $ $Locker:  $
 * @author Harald Albrecht
 */
class JrpcgenVersionInfo implements JrpcgenItem {

	public static class Table extends JrpcgenItemTable<JrpcgenVersionInfo> {}
	
    /**
     * Version number assigned to an ONC/RPC program. This attribute contains
     * either an integer literal or an identifier (which must resolve to an
     * integer).
     */
    public String versionNumber;

    public String resolvedVersionNumber;
    
    /**
     * Identifier assigned to the version number of an ONC/RPC program.
     */
    public String versionId;

    /**
     * Set of procedures specified for a particular ONC/RPC program.
     * The elements in the set are of class {@link JrpcgenProcedureInfo}.
     */
    public JrpcgenProcedureInfo.Table procedures;
    
    /**
     * Constructs a new <code>JrpcgenVersionInfo</code> object containing
     * information about a programs' version and a set of procedures
     * defined by this program version.
     *
     * @param context The context the new version info belongs to.
     * @param versionId Identifier defined for this version of a
     *   particular ONC/RPC program.
     * @param versionNumber Version number.
     * @param procedures Vector of procedures defined for this ONC/RPC program.
     */
    public JrpcgenVersionInfo(JrpcgenContext context, String versionId, String versionNumber, JrpcgenProcedureInfo.Table procedures) {
        this.versionId = versionId;
        this.versionNumber = versionNumber;
        this.procedures = procedures;
        this.context = context;
    }

    @Override
    public String getIdentifier() {
    	return versionId;
    }
    
    public List<JrpcgenConst> getProcedureConstants() {
    	return procedureConstants;
    }
    
    public JrpcgenProcedureInfo completeProcedureInfos() {
    	JrpcgenProcedureInfo conflictingProcedure = null;
    			
    	if ((resolvedVersionNumber != null) && (procedures != null)) {
    		/*
    		 * Loop over the procedures, append the version number to
    		 * the procedure name and check global uniqueness of the
    		 * resulting procedure id.
    		 */
    		Iterator<JrpcgenProcedureInfo> procedureIterator = procedures.iterator();
    		
    		while ((conflictingProcedure == null) && procedureIterator.hasNext()) {
    			JrpcgenProcedureInfo procedure = procedureIterator.next();
    			String procedureId = procedure.procedureId + "_" + resolvedVersionNumber;
    			JrpcgenConst procedureConstant = new JrpcgenConst(context, procedureId, procedure.procedureNumber, context.options().baseClassname);
    			
    			/*
    			 * The current procedure info gets the
    			 * new procedure id in any case.
    			 */
    			procedure.procedureId = procedureId;
    			
    			/*
    			 * Leave the method returning the current procedure info,
    			 * if the constant had a nonunique identifier.
    			 */
    			if (! context.globalDefinitions().addItem(procedureConstant)) {
    				conflictingProcedure = procedure;
    			} else {
    				procedureConstants.add(procedureConstant);
    				procedure.setConstant(procedureConstant);
    			}
    		}
    	} /* endif (The version number is resolved) */
    	
    	return conflictingProcedure;
    }
    
    /**
     * Generate source code for client-side stub methods for a particular
     * remote program version. The client-side stub methods take the
     * parameter(s) from the caller, encode them and throw them over to the
     * server. After receiving a reply, they will unpack and return it as
     * the outcome of the method call.
     *
     * @param javaFile The Java file to write source code into.
     */
    public void writeClientStubMethods(JrpcgenJavaFile javaFile) {
    	for (JrpcgenProcedureInfo procedure : procedures) {
        	String argumentName = null;
        	String resultName = "result$";
        	
        	/*
        	 * Write the method signature.
        	 */
        	procedure.writeClientStubSignature(javaFile.newLine());

            /*
             * Catch the argument name of the XDR argument generated
             * by the procedure.
             */
            argumentName = procedure.writeXdrCallArgument(javaFile, context);
            procedure.writeXdrResultArgument(javaFile, resultName);

            
            //
            // Now emit the real ONC/RPC call using the (optionally
            // wrapped) parameter and (optionally wrapped) result.
            //
            if ( context.options().clampProgAndVers ) {
                javaFile.beginLine().append("client.call(").append(procedure.getConstant().getAsRValue(null))
                	.append(", ").append(JrpcgenConst.getAsRValue(getIdentifier(), null, context))
                	.append(", ").append(argumentName).append(", ").append(resultName).println(");");
            } else {
                javaFile.beginLine().append("client.call(").append(procedure.getConstant().getAsRValue(null))
            		.append(", client.getVersion(), ").append(argumentName).append(", ").append(resultName).println(");");
            }

            /*
             * Write the return statement. Nothing will be written
             * for the result type 'void'.
             */
            procedure.writeReturnStatement(javaFile, resultName);
            
            /*
             * End the procedure.
             */
            javaFile.endBlock().println('}');
    	}
    }
    
    /**
     * Generate public abstract method signatures for all remote procedure
     * calls. This ensures that they have to be implemented before any
     * derived server class gets usefull.
     * 
     * @param out The print writer where the output shall go in.
     * @param versionInfo The cersion information on the RPC program.
     */
    public void writeServerStubMethodCalls(JrpcgenJavaFile javaFile) {
    	/*
    	 * Have a look on the existence of a procedure with procedure
    	 * number 0, the so called null procedure.
    	 * Initially, there is no null procedure.
    	 */
    	boolean hasNullProcedure = false;
    	Iterator<JrpcgenConst> procedureConstant = procedureConstants.iterator();
    	
    	while ((! hasNullProcedure) && procedureConstant.hasNext()) {
    		hasNullProcedure = "0".equals(procedureConstant.next().resolveValue());
    	}
    	
    	javaFile.beginBlock().println("switch ( procedure ) {");

    	/*
    	 * If no null procedure call is specified, the default
    	 * null procedure call is added.
    	 */
    	if (! hasNullProcedure) {
    		javaFile.elseBlock().println("case 0:")
    			.beginLine().println("call.retrieveCall(XdrVoid.XDR_VOID);")
    			.beginLine().println("call.reply(XdrVoid.XDR_VOID);")
    			.beginLine().println("break;");
    	}
    	
    	for (JrpcgenProcedureInfo procedure : procedures) {
    		procedure.writeServerStubMethodCall(javaFile, context);
    	}
    	
        javaFile.elseBlock().println("default:")
    		.beginLine().println("call.failProcedureUnavailable();")
    		.endBlock().println('}');
    }
    
    public void writeServerStubMethods(JrpcgenJavaFile javaFile) {
    	for (JrpcgenProcedureInfo procedure : procedures) {
    		procedure.writeServerStubDeclaration(javaFile, context);
    	}
    }
    
    /**
     * Generates source code to define the version constant belonging to this
     * program.
     *
     * @param out PrintWriter to send source code to.
     */
    public void dumpConstants(PrintWriter out) {
        out.println("    /* ONC/RPC program version number definition */");
        out.println("    public final static int "
                    + versionId + " = " + versionNumber + ";");
    }
    
    public void dump() {
    	dump(System.out).println();
    }
    
    
    public <T extends Appendable> T dump(T appendable) {
    	try {
    		appendable.append("VERSION ").append(versionId)
    			.append(" = ").append(versionNumber).append(JrpcgenContext.newline());

    		for (JrpcgenProcedureInfo procedure : procedures) {
    			procedure.dump(appendable.append("  ")).append(JrpcgenContext.newline());
    		}
    	} catch (IOException ioException) {
    		/// Ignored at this place.
    	}

    	
    	return appendable;    	
    }

    private final JrpcgenContext context;
    private final List<JrpcgenConst> procedureConstants = new LinkedList<JrpcgenConst>();
}

// End of JrpcgenVersionInfo.java