/*
 * 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.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

public class JrpcgenContext implements Closeable {

    /**
     * Contains all global identifiers for type, structure and union specifiers
     * as well as for constants and enumeration members. This static attribute
     * is directly manipulated by the parser.
     */
    private final JrpcgenXdrDefinition.Table globalDefinitions = new JrpcgenXdrDefinition.Table();
    
    /**
     * Contains type identifiers used in a fixed vector context.
     */
    private final Set<String> typesInFixedVectorUse = new HashSet<>();
    
    /**
     * Contains type identifiers used in a dynamic vector context.
     */
    private final Set<String> typesInDynamicVectorUse = new HashSet<>();
    
    /**
     * Contains type names of predefined types. Predefined types are not
     * specified within the x-file processed within this context, but are
     * assumed to exist in the same package implementing the interface
     * 'XdrAble'.
     */
    private final Set<String> predefinedTypes = new HashSet<>();    
    
    /**
     * The x-file to parse (not: the X Files, the latter ones are something
     * completely different).
     */
    public File xFile = null;

	private final JrpcgenOptions options;
	private final FileInputStream xfileIn;
	
    /**
     * Full name of the current source code file.
     */
    public String currentFilename = null;

    public static String newline() {
    	return NEWLINE;
    }
    
    public static JrpcgenContext open(JrpcgenOptions options, File xFile, String jrpcgenVersion) throws IOException {
    	JrpcgenContext context = null;
    	FileInputStream xfileIn = null;
    	
        //
        // Get the base name for the client and server classes, it is derived
        // from the filename.
        //
        if ( options.baseClassname == null ) {
            String name = xFile.getName();
            int dotIdx = name.lastIndexOf('.');
            if ( dotIdx < 0 ) {
                options.baseClassname = name;
            } else {
                options.baseClassname = name.substring(0, dotIdx);
            }
        }

        /*
         * An input stream is required to read the
         * content of the x-file.
         */
        try {
            xfileIn = new FileInputStream(xFile.getCanonicalPath());
            
            context = new JrpcgenContext(options, xFile, xfileIn, jrpcgenVersion);
        } catch ( FileNotFoundException e ) {
            throw(new FileNotFoundException("jrpcgen: can not open source x-file \""
                                            + xFile.getCanonicalPath() + "\""));
        }

        /*
         * Add the basic constants 'TRUE' and 'FALSE'.
         */
        context.globalDefinitions.putItem(new JrpcgenConst(context, "TRUE", "true"));
        context.globalDefinitions.putItem(new JrpcgenConst(context, "FALSE", "false"));
        
    	return context;
    }

    public static JrpcgenContext open(JrpcgenOptions options, String xfilename, String jrpcgenVersion) throws IOException {
    	File xFile;

        /*
         * We accept both, relative and absolute paths.
         * Therefore we look whether or not the filename starts
         * with the separator character.
         * 
         * A relative path will get the current file path as
         * its root.
         */
        if ( xfilename.startsWith(File.separator) )
        {
            /*
             * We got an absolute path.
             */
            xFile = new File(xfilename);
        }
        else
        {
            /*
             * We got a relative path. The current
             * directory will be used as the root.
             */
            xFile = new File(".",xfilename);
        }

        return open(options, xFile, jrpcgenVersion);
    }
    
    final public String getJrpcgenVersion() {
    	return jrpcgenVersion;
    }
    
	final public JrpcgenOptions options() {
		return options;
	}
	
	final public JrpcgenXdrDefinition.Table globalDefinitions() {
		return globalDefinitions;
	}
	
	final public Set<String> typesInFixedVectorUse() {
		return typesInFixedVectorUse;
	}
	
	final public Set<String> typesInDynamicVectorUse() {
		return typesInDynamicVectorUse;
	}
	
	final public Set<String> predefinedTypes() {
		return predefinedTypes;
	}

	public String getInterfaceName(String programId) {
      return options.baseClassname + "_" + programId + "_CltIF";
    }
	
    /**
     * Create a new hash function object and initialize it using a class
     * and package name.
     *
     * @param classname Name of class.
     *
     * @return hash function object.
     */
    public JrpcgenSHA createSHA(String classname) {
    	String packageName = options.packageName;
        JrpcgenSHA hash = new JrpcgenSHA();
        if ( (packageName != null) && (packageName.length() > 0) ) {
            hash.update(packageName + "." + classname);
        } else {
            hash.update(classname);
        }
        return hash;
    }


	
	public void reset() {
		options.reset();
		
        globalDefinitions.clear();
        xFile = null;
        currentFilename = null;
	}
	
	@Override
	public void close() throws IOException {
		xfileIn.close();
	}
	
	private JrpcgenContext(JrpcgenOptions options, File xfile, FileInputStream xfileIn, String jrpcgenVersion) {
		this.options = options;
		this.xFile = xfile;
		this.xfileIn = xfileIn;
		this.jrpcgenVersion = jrpcgenVersion;
	}

	private static final String NEWLINE = System.getProperty("line.separator");
	
	private final String jrpcgenVersion;

}
