/* $Revision: 6707 $ $Author: egonw $ $Date: 2006-07-30 16:38:18 -0400 (Sun, 30 Jul 2006) $
 * 
 * Copyright (C) 2006  Egon Willighagen
 * 
 * Contact: cdk-devel@lists.sourceforge.net
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
 * of the License, or (at your option) any later version.
 * All we ask is that proper credit is given for our work, which includes
 * - but is not limited to - adding the above copyright notice to the beginning
 * of your source code files, and to any copyright notice that you may distribute
 * with programs based on this work.
 * 
 * This program 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package net.sf.cdk.tools;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Class that creates the ${build}/*.javafiles.
 * 
 * @author egonw
 */
public class MakeJavafilesFiles {

    private Map<String,List<String>> cdkPackages;
    private Map<String,List<String>> cdkSets;
    
    private String default_module;

    /** Java files that should not be processed because they are
     * listed in a *.autogenerated file.
     */
    private List<String> blacklist;
   
    private String sourceDir = null;
    private String outputDir = null;
    private String metainfDir = null;

    public MakeJavafilesFiles(String metainfDir, String sourceDir, String outputDir) {
        cdkPackages = new Hashtable<String,List<String>>();
        cdkSets = new Hashtable<String,List<String>>();
        this.sourceDir = sourceDir;
        this.outputDir = outputDir;
        this.metainfDir = metainfDir;
        readBlackList();
        if (sourceDir.contains("test")) {
            default_module = "test-extra";
        } else {
            default_module = "extra";
        }
    }
	
    private void readBlackList() {
    	blacklist = new ArrayList<String>();
		String metainfDirPath = this.metainfDir;
		File metinfDir = new File(metainfDirPath);
		File[] files = metinfDir.listFiles();
		for (int i=0; i<files.length; i++) {
			if (files[i].getName().endsWith(".autogenerated")) {
				// add content to blacklist
				try {
					BufferedReader reader = new BufferedReader(new FileReader(files[i]));
					String line = reader.readLine();
					while (line != null) {
						line.trim();
						if (line.length() > 0) 
							blacklist.add(line);
						line = reader.readLine();
					}
				} catch (Exception e) {
					System.out.println("Error reading a *.autogenerated file: " + e.getMessage());
					e.printStackTrace();
					System.exit(1);
				}
			}
		}
		
	}

	public void outputResults() {
        // output information in .javafiles and .classes files
        try {
			Iterator<String> keys = cdkPackages.keySet().iterator();
			while (keys.hasNext()) {
			    String key = (String)keys.next();
			    
			    // create one file for each cdk package = key
			    PrintWriter outJava = new PrintWriter(
			    	new FileWriter(outputDir + "/" + key + ".javafiles")
			    );
			    PrintWriter outClass = new PrintWriter(
			    	new FileWriter(outputDir + "/" + key + ".classes")
			    );
			    List<String> packageClasses = cdkPackages.get(key);
			    Iterator<String> classes = packageClasses.iterator();
			    while (classes.hasNext()) {
			        String packageClass = toAPIPath(classes.next());
			        if (!blacklist.contains(packageClass + ".java")) {
			        	outJava.println(packageClass + ".java");
			        	outClass.println(packageClass + "*.class");
			        }
			    }
			    outJava.flush(); outJava.close();
			    outClass.flush(); outClass.close();
			}
	        // output information in .set files
	        keys = cdkSets.keySet().iterator();
	        while (keys.hasNext()) {
	            String key = (String)keys.next();
	            
	            // create one file for each cdk package = key
	            PrintWriter outJava = new PrintWriter(
	            	new FileWriter(outputDir + "/" + key + ".set")
	            );
	            List<String> packageClasses = cdkSets.get(key);
	            Iterator<String> classes = packageClasses.iterator();
	            while (classes.hasNext()) {
	                String packageClass = (String)classes.next();
	                outJava.println(packageClass);
	            }
	            outJava.flush(); outJava.close();
	        }
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    
    public void processJavaSourceFiles(File path) {
    	if (path.isDirectory()) {
    		File[] files = path.listFiles();
    		for (int i=files.length;i>0;i--) {
    			processJavaSourceFiles(files[i-1]);
    		}
    	} else if (path.isFile() && path.getPath().endsWith(".java") &&
    			   !(path.getPath().indexOf("net/sf") != -1 ||
                             path.getPath().indexOf("net\\sf") != -1)) {
    		String[] moduleAndSet = getModuleAndSet(path);
    		if (moduleAndSet == null) {
    			System.out.println("Something wrong with the Java source file: " + path);    			
    		} else {
    			if (moduleAndSet[0] != null) {
        			addClassToCDKPackage(getSourceName(path), moduleAndSet[0]);
    			}
    			if (moduleAndSet[1] != null) {
    				addClassToCDKSet(getClassName(path), moduleAndSet[1]);
    			}
    		}
    	}
    }
    
    public String[] getModuleAndSet(File file) {
    	try {
    		String[] results = new String[2];
            results[0] = default_module;
    		results[1] = null;
			BufferedReader reader = new BufferedReader(
				new FileReader(file)
			);
			String line = null;
			boolean inComment = false;
			while ((line = reader.readLine()) != null) {
				int index = line.indexOf("/**");
				if (index != -1) {
					inComment = true;
					if (line.substring(index).indexOf("*/") != -1) inComment = false;
				} else {
					if (line.indexOf("*/") != -1) inComment = false;
				}
				
				if (!inComment && (line.indexOf("public class") != -1 ||
                                                line.indexOf("public enum") != -1 ||
						line.indexOf("public interface") != -1 ||
						line.indexOf("public @interface") != -1 ||
						line.indexOf("abstract class") != -1 ||
						line.indexOf("final class") != -1)) {
					// Nothing specified: return the default 'extra'
					reader.close();
					return results;
				}
				
				index = line.indexOf("@cdk.module");
				String name = "";
				if (index != -1) {
					index += 11;
					// skip the first chars
					while (Character.isWhitespace(line.charAt(index))) index++;
					while (index < line.length() && 
						   !Character.isWhitespace(line.charAt(index))) {
						name += line.charAt(index);
						index++;
					}
					results[0] = name;
				} else {
					index = line.indexOf("@cdk.set");
					String set = "";
					if (index != -1) {
						index += 11;
						// skip the first chars
						while (Character.isWhitespace(line.charAt(index))) index++;
						while (index < line.length() && 
							   !Character.isWhitespace(line.charAt(index))) {
							set += line.charAt(index);
							index++;
						}
						results[1] = set;
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return null;
    }
    
	public static void main(String[] args) {
		if (args.length != 3) {
			System.out.println("Syntax: MakeJavafilesFiles <metainfDir> <sourceDir> <outputDir>");
			System.exit(-1);
		}
		
		MakeJavafilesFiles processor = new MakeJavafilesFiles(args[0], args[1], args[2]);
		
		processor.processJavaSourceFiles(new File(args[1]));
		processor.outputResults();
		
	}
	
    private String toAPIPath(String className) {
        StringBuffer sb = new StringBuffer();
        for (int i=0; i<className.length(); i++) {
            if (className.charAt(i) == '.') {
                sb.append('/');
            } else {
                sb.append(className.charAt(i));
            }
        }
        return sb.toString();
    }

    private String getSourceName(File classFile) {
    	// assume the pattern src/package/className.java
    	// return package/className
    	String tmp = classFile.getPath().substring(sourceDir.length()+1); 
        return tmp.substring(0, tmp.length()-5);
    }

    private String getClassName(File classFile) {
    	// assume the pattern src/package/className.java
    	// return package.className
    	StringBuffer sb = new StringBuffer();
    	String className = classFile.getPath().substring(sourceDir.length()+1);
        for (int i=0; i<className.length()-5; i++) {
            if (className.charAt(i) == '/' || className.charAt(i) == '\\') {
                sb.append('.');
            } else {
                sb.append(className.charAt(i));
            }
        }
        return sb.toString();
    }

    private void addClassToCDKPackage(String packageClass, String cdkPackageName) {
        List<String> packageClasses = cdkPackages.get(cdkPackageName);
        if (packageClasses == null) {
            packageClasses = new ArrayList<String>();
            cdkPackages.put(cdkPackageName, packageClasses);
        }
        packageClasses.add(packageClass);
    }

    private void addClassToCDKSet(String packageClass, String cdkPackageName) {
        List<String> packageClasses = cdkSets.get(cdkPackageName);
        if (packageClasses == null) {
            packageClasses = new ArrayList<String>();
            cdkSets.put(cdkPackageName, packageClasses);
        }
        packageClasses.add(packageClass);
    }

}
