import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/*
 * Created on May 21, 2005
 */

/**
 * Class scans given directory for sourcefiles and appends a disclaimer
 *
 * This is a quick hack version of a good appending methods and it is NOT memory efficient. Thats
 * why "Start Wars - Episode I" will not work as disclaimer. The disclaimer itself should NOT be inlined
 * in sourcecode comment tags, this is done by {@see #readDiscalimer()} (Let it be the pure disclaimer, without any \/* *\/). 
 * Also the size of source files is limited to the heap size of the vm. If one uses this with large source
 * or disclaimer files extend the VM max heap size (e.g. -Xmx512m).
 * 
 * Remember, the scanFile() method automatically appends "\r\n" to each line while writing the files.
 * 
 * parameters
 * -d <src directory> The source directory
 * -f <disclaimer file> The disclaimer
 * -v <0..2> verbose output
 * @author Thasso
 */
public class AppendDisclaimer {
    /**
     * the input directory
     */
    public static String inputDir = ".";
    /**
     * Filename of the disclaimer
     */
    public static String disclaimerFile = null;
    /**
     * String representation of the disclaimer
     */
    public static String disclaimer;
    
    /**
     * verbose level
     */
    public static int verbose = 0;
        
    public static void main(String[] args) {
        if(args.length < 1){
            printUsage();
        }
        // Parse arguments
        for (int i = 0; i < args.length; i++) {
            if(args[i].equalsIgnoreCase("-d")){
                inputDir = args[i+1];
            }
            if(args[i].equalsIgnoreCase("-v")){
                verbose = args[i+1] != null ? Integer.parseInt(args[i+1]):2;
            }
            if(args[i].equalsIgnoreCase("-f")){
                disclaimerFile = args[i+1];
            }
        }
        if(disclaimerFile == null)
            printUsage("Discalimer file not set ! Use -f to define a disclaimer");
        
        readDiscalimer();        
        
        // read filelist from directory
        File input = new File(inputDir);
        if(!input.isDirectory() ||!input.canRead()){
            printUsage("Can not read from directory " + input.getAbsolutePath());            
        }        
        if(verbose > 0)
            System.out.println("Scanning Directory :\n\t"+input.getAbsolutePath());
        
        List javaFiles = readDirectory(input, ".java");        
        //Scan files and append disclaimer               
        for (Iterator it = javaFiles.iterator(); it.hasNext();) {
            File f = (File) it.next();
            scanFile(f);
        }              
    }
    
    /**
     * read the disclaimer from file
     */
    private static void readDiscalimer() {
        File f = new File(disclaimerFile);
        StringBuffer d = new StringBuffer();        
        if(!f.exists())
            printUsage("Disclaimer " + disclaimerFile + " not found");
        if(!f.canRead())
            printUsage("Disclaimer " + disclaimerFile + " not readable");
        
        try {
            BufferedReader in = new BufferedReader(new FileReader(f));
            d.append("/*\r\n");
            String l = in.readLine();
            while(l != null){
                d.append(" * "+l+"\r\n");
                l = in.readLine();
            }
            d.append("*/\r\n");
            disclaimer = d.toString();            
            in.close();
        } catch (FileNotFoundException e) {
            printUsage("Disclaimer " + disclaimerFile + " not found");
        } catch (IOException e) {
            printUsage("Error while reading from file " + disclaimerFile);
        }        
    }

    /**
     * Memory consuming scna file method.
     * Creates a buffer and appends the disclaimer. Than reads the file and appends it to
     * the buffer, line by line, adding \\r\\n to each line.
     * When done, writes out the file with a simple and absolute inefficent buffer.toString() call.
     * 
     * @param f
     */
    private static void scanFile(File f){
        BufferedReader in = null;
        StringBuffer content = new StringBuffer();
        content.append(disclaimer+"\r\n"+"\r\n");
        if(verbose > 1)
            System.out.println("Reading file " + f.getName());
        
        try {
            in = new BufferedReader(new FileReader(f));            
            String line = in.readLine();
            while(line != null){
                content.append(line+"\r\n");
                line = in.readLine();
            }
            in.close();
        } catch (FileNotFoundException e) {
            printUsage("File  not found " + f.getName());
        } catch (IOException e) {
            printUsage("Could not read file " + f.getName());
        }finally{
            try {
                in.close();
            } catch (IOException e1) {}
        }        
        if(verbose > 1)
            System.out.println("Writing file " + f.getName());
        
        BufferedWriter w = null;
        try {
            w = new BufferedWriter(new FileWriter(f));
            w.write(content.toString());
            w.close();
        } catch (IOException e) {
            printUsage("Could not write file " + f.getName());
        }finally{
            try {
                w.close();
            } catch (IOException e) {}
        }
    }


    /**
     * Recurcive scan directory for files with given suffix
     * 
     * @param dir directory to scan
     * @param suffix files suffix
     */
    protected static List readDirectory(File dir, String suffix) {
        ArrayList files = new ArrayList();
        if(dir.isFile())
            return null;
        
        if(verbose > 1)
            System.out.println("Scanning Directory :\n\t"+dir.getAbsolutePath());

        File[] all = dir.listFiles();
        ArrayList dirs = new ArrayList();
        for (int i = 0; i < all.length; i++) {
            if(all[i].isFile()){
                if(all[i].getName().endsWith(suffix)){
                    files.add(all[i]);
                }
            }else if(all[i].isDirectory()){                
                dirs.add(all[i]);
            }                
        }
        for (Iterator it = dirs.iterator(); it.hasNext();) {
            File d = (File) it.next();
            List l = readDirectory(d, suffix);
            files.addAll(l);
        }
        return files;
    }
    

    /**
     * print string and the usage
     * @param string
     */
    private static void printUsage(String string) {
        System.err.println(string + "\n\n");
        printUsage();
    }

    /**
     * print the usage
     */
    private static void printUsage() {        
        System.err.println(""
                +" This is a quick hack version of a good appending methods and it is NOT memory efficient. \r\n"                        
                + " Thats why \"Start Wars - Episode I\" will not work as disclaimer.\r\n"
                + " The disclaimer itself should NOT be inlined\r\n"
                + " in sourcecode comment tags, this is done automaticaly (Let it be the pure disclaimer, without any /* */) \r\n"
                + " Also the size of source files is limited to the heap size of the vm. If one uses this with large source\r\n"
                + " or disclaimer files extend the VM max heap size (e.g. -Xmx512m).\r\n"
                + " \r\n"
                + " Remember, the scanFile() method automatically appends \"\\r\\n\" to each line while writing the files.\r\n"
                + " \r\n"
                + " parameters\r\n"
                + " -d <src directory> The source directory\r\n"
                + " -f <disclaimer file> The disclaimer\r\n"
                + " -v <0..2> verbose output\r\n"

        );
        System.exit(1);
    }

}

