/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
package uk.ac.ic.doc.kevlar.build;

import java.io.File;
import java.io.FileFilter;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import uk.ac.ic.doc.kevlar.publicInterfaces.AbstractProgram;
import uk.ac.ic.doc.kevlar.publicInterfaces.IType;

/**
 * A Utility Program to generate an XML file representing a directory structure,
 * for use in the build system.
 * 
 * Note, I thought this was only a simple program, hence the lack of OO. Next
 * time I add anything to it, it will get more OOy :)
 * 
 * @author tora
 */
public class XMLDirGenerator {

	private static final String CORE = "uk" + File.separatorChar + "ac" + 
										File.separatorChar + "ic" + File.separatorChar + 
										"doc" + File.separatorChar + "kevlar" + File.separatorChar + "core" + File.separatorChar;
	
    private static final String CORE_TYPE = CORE + "types" + File.separatorChar;

    private static final String CORE_PROGRAMS = CORE + "programs" + File.separatorChar;

    private static final String AN_ERROR_OCCURED = "An error occured";

    private static final String SPLIT_POINT = "=";

    static final Set<String> BAD_FILE_SET = new HashSet<String>();

    /**
	 * 
	 * @param args
	 *            The arguments to the function.
	 * @throws Exception
	 *             If anything goes wrong.
	 */
    public static void main(String[] args) throws Exception {

        if (args.length < 2) {
            usage();
            return;
        }

        BAD_FILE_SET.add("CVS");

        List<String> argList = new LinkedList<String>(Arrays.asList(args));

        Map<String, String> mappings = new HashMap<String, String>();

        String output = argList.get(0);
        argList.remove(0);

        for (String s : argList) {
            String[] parts = s.split(SPLIT_POINT, 0);
            if (parts.length != 2) {
                usage();
                return;
            }
            mappings.put(parts[0], parts[1]);
        }

        PrintWriter outStream;

        if (output.equals("-")) {
            outStream = new PrintWriter(System.out);
        } else {
            outStream = new PrintWriter(new File(output));
        }

        preamble(outStream);

        for (String key : mappings.keySet()) {
            String val = mappings.get(key);
            File dir = new File(key);
            doOneArea(dir, val, outStream);
        }

        outStream.println("</dirs>");
        outStream.close();
    }

    private static void preamble(PrintWriter pw) {
        pw.println("<?xml version='1.0'?>");
        pw.println("<dirs>");
    }

    private static void doOneArea(File dir, String val, PrintWriter pw) {
        if (!dir.isDirectory()) {
            throw new IllegalArgumentException(dir + "is not a directory");
        }

        File[] kids = dir.listFiles(new FileFilter() {

            /**
			 * @see java.io.FileFilter#accept(java.io.File)
			 */
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    return !BAD_FILE_SET.contains(pathname.getName());
                }

                return false;
            }
        }

        );

        pw.println("<" + val + ">");

        for (File f : kids) {
            pw.println("\t<dir>"  );
            pw.println("\t\t<name>" + f.getName() + "</name>");
            File[] classKids = f.listFiles(new FileFilter() {

                /**
				 * @see java.io.FileFilter#accept(java.io.File)
				 */
                public boolean accept(File pathname) {
                    return pathname.canRead() && pathname.isFile()
                            && pathname.getName().endsWith("class");
                }

            });

            ClassLoader cl;
            try {
                cl = new URLClassLoader( new URL[]{ f.toURL() });

                for (File aClassFile : classKids) {
                    try {

                        if (f.getAbsolutePath().contains(CORE_PROGRAMS)) {

                            Class cP = cl.loadClass(CORE_PROGRAMS.replace(File.separatorChar, '.')
                                    + f.getName()
                                    + "."
                                    + aClassFile.getName().substring(
                                            0,
                                            aClassFile.getName().lastIndexOf(
                                                    '.')));

                            if (cP.getSuperclass() != null && cP.getSuperclass().equals(AbstractProgram.class)) {
                                pw.println("\t\t<program>" + cP.getCanonicalName() + "</program>");
                            }

                        }

                        if (f.getAbsolutePath().contains(CORE_TYPE)) {

                            Class cT = cl.loadClass(CORE_TYPE.replace(File.separatorChar, '.')
                                    + f.getName()
                                    + "."
                                    + aClassFile.getName().substring(
                                            0,
                                            aClassFile.getName().lastIndexOf(
                                                    '.')));
                            if (cT.isInterface()
                                    && IType.class.isAssignableFrom(cT)) {
                                pw.println("\t\t<type>" + cT.getCanonicalName() + "</type>");
                            }

                        }

                    } catch (Exception e) {
                        System.err.println(AN_ERROR_OCCURED);
                        System.err.println(e);
                    } catch (Error e) {
                        System.err.println(AN_ERROR_OCCURED);
                        System.err.println(e);
                    }
                }
            } catch (MalformedURLException mue) {
                System.err.println(AN_ERROR_OCCURED);
                System.err.println(mue);
            }
            pw.println( "\t</dir>" );
        }

        pw.println("</" + val + ">");
    }

    private static void usage() {
        System.out.println("XMLDirGenerator: output dirpath" + SPLIT_POINT
                + "xmlname [ dirpath" + SPLIT_POINT + "xmlname ]+");
        System.out.println("output can be a file, or '-' for stdout");
    }

}
