/*
 * Copyright (c) 2021
 * NDE Netzdesign und -entwicklung AG, Hamburg, 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;

public class JrpcgenTypedefinition extends JrpcgenComplexType {

	public JrpcgenTypedefinition(JrpcgenDeclaration declaration, JrpcgenContext context) {
		super(declaration.getIdentifier(), Type.TYPEDEF);
		this.context = context;
		this.declaration = new TypedefDeclaration(declaration.getTypeMapping(),
				declaration.getKind(), declaration.getSize());
	}

	final public JrpcgenDeclaration getDeclaration() {
		return declaration;
	}
	
	final public String getTypename() {
		return declaration.getType();
	}
	
    /**
     * Generate a source code file containing a wrapper class for a typedef
     * defined in a x-file.
     */
	@Override
    public void generateJavaFile() {
    	//
    	// Create new source code file containing a Java class representing
    	// the XDR type definition. Type definitions are modeled as structures
		// wrapping a value of the defined type. For this a wrapper structure
		// is defined with a single declaration given by the type definition
		// declaration.
    	//
    	JrpcgenDeclaration.Table declarations = new JrpcgenDeclaration.Table();
    	JrpcgenStruct wrapperStruct = new JrpcgenStruct(context, getIdentifier(), declarations);
    	
    	/*
    	 * In contrast to structure definitions the code generation of type definitions
    	 * cannot be controlled by the options 'makeBean' and 'noValueCtor':
    	 * - Type definitions are not generated as Java beans
    	 * - Type definitions always get a value constructor.
    	 * The given values are reminded, set accordingly in the context
    	 * and are restored in the context afterwards. 
    	 */
    	JrpcgenOptions options = context.options();
    	boolean makeBeanOptionGiven = options.makeBean;
    	boolean noValueCtorOptionGiven = options.noValueCtor;
    	
    	/*
    	 * Add the type definition declaration to the
    	 * declaration table of the wrapper structure.
    	 */
    	declarations.putItem(declaration);
    	
    	/*
    	 * Set the options 'makeBean' and 'noValaueCtor' accordingly
    	 * to the needs of a type definition.
    	 */
    	options.makeBean = false;
    	options.noValueCtor = false;
    	
    	/*
    	 * Let the wrapper structure generate the
    	 * Java file for the type definition.
    	 */
    	wrapperStruct.generateJavaFile();

    	/*
    	 * Restore the options 'makeBean' and 'noValueCtor'
    	 * to their previous values.
    	 */
    	options.makeBean = makeBeanOptionGiven;
    	options.noValueCtor = noValueCtorOptionGiven;
    }

	public void dump() {
		System.out.append(Type.TYPEDEF.name()).append(' ');
		dump(System.out).println();		
	}
	
    public <T extends Appendable> T dump(T appendable) {
    	try {
    		appendable
    			.append(declaration.getType())
    			.append(JrpcgenDeclaration.Kind.INDIRECTION.equals(declaration.getKind()) ? " *" : " ")
    			.append(getIdentifier());
    		
        	switch (declaration.getKind()) {
        	case FIXEDVECTOR:
        		appendable.append('[').append(declaration.getSize()).append(']');
        		break;
        		
        	case DYNAMICVECTOR:
        		if (declaration.getSize() == null) {
        			appendable.append("<>");
        		} else {
        			appendable.append('<').append(declaration.getSize()).append('>');
        		}
        		break;
        		
        	default:
        		break;
        	}
    	} catch (IOException ioException) {
    		// Ignored at this place.
    	}
    	
    	return appendable;
    }

	/**
	 * Specialisation of class JrpcgenDeclaration.
	 * 
	 * <p>The specialisation has the purpuse to override method {@code updateHash()}, only.
	 * With this specialisation a generated serial version UID of the representation of a
	 * type definition gets the same compared to older versions of the jrpcgen-library.
	 * Especially, the identifier 'value' of an instance of this class will not be considered
	 * when building the hash value representing the serial version UID.
	 */
	private class TypedefDeclaration extends JrpcgenDeclaration {

		public TypedefDeclaration(JrpcgenTypeMapping typeMapping, JrpcgenDeclaration.Kind kind, String size) {
			super("value", typeMapping, kind, size);
		}

		public void updateHash(JrpcgenSHA hash) {
			hash.update(getType());
			hash.update(getKind().ordinal());
		}
	}

	private final JrpcgenContext context;
	private final JrpcgenDeclaration declaration; 
}
