import java.util.*;
import java.io.*;

public class ManifestUtil
{
        private String CSVFile;
        private String WLFile;
        private String EARPath;
        private String RootPath;
        private String ExtRepo;
        private boolean _debug;

        private static class CLIParse
        {
                private Vector argv = new Vector();
                private HashMap argsMap = new HashMap();
                private int argvIndex = 0;

                public CLIParse(String[] args)
                {
                        for (int i = 0; i < args.length; i++) {
                                if (args[i].startsWith("-")) {
                                        int ix = args[i].indexOf("=");
                                        String key = (ix > 0) ? args[i].substring(1, ix) : args[i].substring(1);
                                        String value = (ix > 0) ? args[i].substring(ix+1) : "";
                                        argsMap.put(key.toLowerCase(), value);
                                }
                                else {
                                        argv.addElement(args[i]);
                                }
                        }
                }

                public boolean hasArg(String arg)
                {
                        return argsMap.containsKey(arg.toLowerCase());
                }

                public String getArg(String arg)
                {
                        return (String)argsMap.get(arg.toLowerCase());
                }
                public String nextParam()
                {
                        String strReturn = null;
                        if (argvIndex < argv.size()) {
                                strReturn = (String)argv.elementAt(argvIndex++);
                        }
                        return strReturn;
                }
        }

        private static void printUsage()
        {
                System.out.println ("Usage:");
                System.out.println ("manifestUtil [-csv=manifest.csv] [-ear=path-to-EAR-folder] [-root=path-to-project-root-folder] [-wlist=whitelist.txt] [-repo=path-to-external-mvn-repo-folder] [-debug]");
        }

        private static boolean validate(CLIParse parser)
        {
                boolean fOK = true;
                if ((! parser.hasArg("csv")) || (! new File(parser.getArg("csv")).exists()))
                {
                        System.out.println ("Can't find manifest csv file.");
                        fOK = false;
                }

                if ((! parser.hasArg("ear")) || (! new File(parser.getArg("ear")).exists()))
                {
                        System.out.println ("Can't find the EAR folder.");
                        fOK = false;
                }

                if ((! parser.hasArg("root")) || (! new File(parser.getArg("root")).exists()))
                {
                        System.out.println ("Can't find the project root folder.");
                        fOK = false;
                }

                if (parser.hasArg("repo") && (! new File(parser.getArg("repo")).exists()))
                {
                        System.out.println ("Can't find the external repo folder.");
                        fOK = false;
                }

                if (!fOK)
                {
                        printUsage();
                }

                return fOK;
        }

        public ManifestUtil(String csv, String ear, String root, String wlist, String repo, boolean debug)
        {
                CSVFile = csv;
                EARPath = ear;
                RootPath = root;
                WLFile = wlist;
                ExtRepo = repo;
                _debug = debug;
        }

        private String printStrArr(String[] arr, String seperator)
        {
                if (arr.length < 1)
                {
                        return "arr is empty!";
                }

                StringBuffer result = new StringBuffer();
                for (int i=0; i<arr.length; i++) {
                        result.append(arr[i] + seperator);
                }

                return result.toString();
        }

        private String printStrArr(String[] arr)
        {
                if (arr.length < 1)
                {
                        return "arr is empty!";
                }

                return printStrArr(arr, "#");
        }

        private String[] rmFromStrArr(String[] arr, String del)
        {
                List result = new LinkedList();
                for (String item : arr) {
                        if (item != null && !item.equals(del))
                        {
                                result.add(item);
                        }
                }
                return (String[])result.toArray(arr);
        }

        public HashSet getFileWhiteList()
        {
                BufferedReader br = null;
                HashSet hsReturn = new HashSet();
                try
                {
                        br = new BufferedReader(new FileReader(WLFile));
                        String row = "";
                        while ((row = br.readLine()) != null) {
                                row = row.trim();
                                if (
                                        !row.startsWith("#") &&
                                        !row.startsWith("*") &&
                                        row.length() > 0
                                ) {
                                        hsReturn.add(row);
                                }
                        }
                } // end try
                catch (IOException e) {
                        System.err.println("Error: " + e);
                }
                finally {
                        try {
                                if (br != null)
                                {
                                        br.close();
                                }
                        } catch (Exception e) {}
                }

                if (_debug) {
                        System.out.println ("getFileWhiteList return:\n" + hsReturn.toString());
                }
                return hsReturn;
        }

        public HashSet getMVNWhiteList()
        {
                BufferedReader br = null;
                HashSet hsReturn = new HashSet();
                try
                {
                        br = new BufferedReader(new FileReader(WLFile));
                        String row = "";
                        while ((row = br.readLine()) != null) {
                                row = row.trim();
                                if (
                                        row.startsWith("*")
                                ) {
                                        hsReturn.add(row.replace ("*", ""));
                                }
                        }
                } // end try
                catch (IOException e) {
                        System.err.println("Error: " + e);
                }
                finally {
                        try {
                                if (br != null)
                                {
                                        br.close();
                                }
                        } catch (Exception e) {}
                }

                if (_debug) {
                        System.out.println ("getMVNWhiteList return:\n" + hsReturn.toString());
                }
                return hsReturn;
        }


        public HashSet getDataSet()
        {
                BufferedReader br = null;
                HashSet hsReturn = new HashSet();
                try
                {
                        br = new BufferedReader(new FileReader(CSVFile));
                        String row = "";
                        int csvLinecount = 1;
                        while ((row = br.readLine()) != null) { // while loop begins here
                                String[] csvParts = row.split(",");
                                if (
                                        csvParts != null && 
                                        csvParts.length>2 && 
                                        row.indexOf("deploy")>-1 &&
                                        row.indexOf("ovirt-engine:") == -1
                                ) {
                                        String[] parts = csvParts[0].trim().split(":");

                                        if (parts.length>2)
                                        {
                                                parts = rmFromStrArr(parts, "jar");
                                                parts = rmFromStrArr(parts, "ejb");
                                                parts = rmFromStrArr(parts, "war");
                                                if (_debug) {
                                                        System.out.println ("CSV:File name:" + parts[1] + " version:" + parts[2]);
                                                }
                                                String ver = (parts[2] != null && parts[2] != "") ? "-" + parts[2] : "";
                                                hsReturn.add(parts[1] + ver);
                                        }
                                        else {
                                                System.out.println("SKIPPING artifact with <=2 parts: {" + printStrArr(parts, ":") + "} CSV line(" + csvLinecount + "): " + printStrArr(csvParts, ", "));
                                        }
                                }
                                else {
                                        System.out.println("SKIPPING CSV line#" + csvLinecount + " with fields:" + printStrArr(csvParts));
                                }
                                csvLinecount++;
                        } // end while
                } // end try
                catch (IOException e) {
                        System.err.println("Error: " + e);
                }
                finally {
                        try {
                                if (br != null)
                                {
                                        br.close();
                                }
                        } catch (Exception e) {}
                }

                if (_debug) {
                        System.out.println ("getDataSet return:\n" + hsReturn.toString());
                }
                return hsReturn;
        }

        public void addFiles(String strPath, Set set) throws Exception
        {
                if (_debug) {
                        System.out.println("Scanning directory " + strPath);
                }

                File[] listOfFiles = new File(strPath).listFiles();
                if (listOfFiles == null || listOfFiles.length == 0)
                {
                        if (_debug) {
                                System.out.println("\t" + strPath + " has no files inside!");
                        }

                        if (new File(strPath).isFile() && strPath.indexOf(".jar") != -1)
                        {
                                String name = strPath.replace(".jar", "");
                                set.add(name);
                                if (_debug) {
                                        System.out.println(" Adding " + name);
                                }
                        }
                        return;
                 }

                for (int i = 0; i < listOfFiles.length; i++) {
                        if (_debug) {
                                System.out.print("Checking " + listOfFiles[i].getName());
                        }

                        if (listOfFiles[i].isFile() && listOfFiles[i].getName().indexOf(".jar") != -1) {
                                String name = listOfFiles[i].getName().replace(".jar", "");
                                set.add(name);
                                if (_debug) {
                                        System.out.print("Checking " + listOfFiles[i].getName());
                                }
                                if (_debug) {
                                        System.out.println(" Adding " + name);
                                }
                        } else if (listOfFiles[i].isDirectory()) {
                                if (_debug) {
                                        System.out.println("\tEntering directory " + listOfFiles[i].getName());
                                }
                                addFiles(listOfFiles[i].getCanonicalFile().getPath(), set);
                        }
                }
        }

        public boolean testFiles(HashSet dataSet) throws Exception
        {
                boolean fReturn = true;
                HashSet set = new HashSet();

                addFiles(EARPath, set);
                Set<String> onlyCSV = new HashSet<String>(dataSet);
                onlyCSV.removeAll(set);

                //Remove whitelisted items
                Set<String> white = getFileWhiteList();
                for (String wlItem : white) {
                        if (onlyCSV.contains(wlItem)) {
                                onlyCSV.remove(wlItem);
                                System.out.println ("* Ignoring whitelist item: " + wlItem);
                        }
                        //else {
                        //        System.out.println ("Skipping whitelist item: " + wlItem);
                        //}
                }

                String stra[] = (String[])onlyCSV.toArray(new String[0]);
                Arrays.sort(stra);
                fReturn = (fReturn && (onlyCSV.size() == 0));

                System.out.println ("-------------------------");
                System.out.println("Only in CSV: " + onlyCSV.size() + " files.");
                System.out.println ("-------------------------");
                if (onlyCSV.size() > 0)
                {
                        System.out.println ("\t" + printStrArr(stra, "\n\t"));
                        if (_debug) {
                                System.out.println(onlyCSV);
                        }
                }
                else {
                        System.out.println ("\t Test passed successfully :)");
                }

                System.out.println("\n");

                //Destroys set, but we do not need it any more.
                set.removeAll(dataSet);
                //Remove whitelisted items
                for (String wlItem : white) {
                        if (set.contains(wlItem)) {
                                set.remove(wlItem);
                                System.out.println ("* Ignoring whitelist item: " + wlItem);
                        }
                        //else {
                        //        System.out.println ("Skipping whitelist item: " + wlItem);
                        //}
                }
                stra = (String[])set.toArray(new String[0]);
                Arrays.sort(stra);
                fReturn = (fReturn && (set.size() == 0));

                System.out.println ("-------------------------");
                System.out.println("\nOnly in EAR Folder: " + set.size() + " files.");
                System.out.println ("-------------------------");
                if (set.size() > 0)
                {
                        System.out.println ("\t" + printStrArr(stra, "\n\t"));
                        if (_debug) {
                                System.out.println(set);
                        }
                }
                else {
                        System.out.println ("\t Test passed successfully :)");
                }

                return fReturn;
        }

        public boolean testMVN(HashSet dataSet) throws java.io.IOException, java.lang.InterruptedException
        {
                // The mvn structure groupId:artifactId:packaging:classifier:version:scope
                boolean fReturn = true;
                HashSet setMVN = new HashSet();
                String s;
                String maven = "mvn";
                if (System.getProperty("os.name").startsWith("Win")) {
                        maven += ".bat";
                }
                maven += _debug ? " -X" : "";

                if (ExtRepo != null && ExtRepo != "") {
                        maven += " -D maven.repo.local=" + ExtRepo;
                }

                Process p = Runtime.getRuntime().exec(maven + " dependency:list", null, new File(RootPath));
                BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
                BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));

                // read the output from the command
                //System.out.println("Here is the standard output of the command:\n");
                while ((s = stdInput.readLine()) != null)
                {
                        s = s.replace("[INFO]    ", "");
                        s = s.replace("[INFO] ", "");
                        String[] parts = s.trim().split(":");
                        if (_debug) {
                                System.out.println("checking:" + printStrArr(parts) + "\t");
                        }
                        if (
                                parts != null && parts.length>2 &&
                                s.indexOf(":test") == -1 &&
                                s.indexOf(":provided") == -1 &&
                                s.indexOf(".engine.") == -1 &&
                                s.indexOf('[') == -1 &&
                                s.indexOf("artifact") == -1 &&
                                s.indexOf("Downloading:") == -1 &&
                                s.indexOf("Unable to find") == -1 &&
                                s.indexOf("(default-cli) @") == -1 &&
                                parts[2].indexOf(']') == -1 &&
                                parts[0].indexOf("Finished at") == -1
                        ) {
                                parts = rmFromStrArr(parts, "jar");
                                parts = rmFromStrArr(parts, "ejb");
                                parts = rmFromStrArr(parts, "war");
                                //System.out.println("Adding: " + parts[1] + "-" + parts[2]);
                                if (parts.length==6) // Structure groupId:artifactId:classifier:version:scope
                                {
                                        setMVN.add(parts[1] + "-" + parts[3]);
                                }
                                else // Structure groupId:artifactId:version:scope
                                {
                                        setMVN.add(parts[1] + "-" + parts[2]);
                                }
                        }
                        else {
                                if (_debug) {
                                        System.out.println("SKIPPING Maven entry: " + s);
                                }
                        }
                }

                // Read any errors from the attempted command
                while ((s = stdError.readLine()) != null) {
                        System.out.println("STDERR: " + s);
                }

                p.waitFor();
                if (p.exitValue() != 0)
                {
                        System.out.println("/###################################################\\");
                        System.out.println("| Maven dependencies check FAILED with exit code: " + p.exitValue() + " |");
                        System.out.println("\\###################################################/");
                        fReturn = false;
                }

                if (fReturn)
                {
                        Set<String> onlyCSV = new HashSet<String>(dataSet);
                        onlyCSV.removeAll(setMVN);

                        //Remove whitelisted items
                        Set<String> white = getMVNWhiteList();
                        for (String wlItem : white) {
                                if (onlyCSV.contains(wlItem)) {
                                        onlyCSV.remove(wlItem);
                                        System.out.println ("* Ignoring mvn whitelist item: " + wlItem);
                                }
                                //else {
                                //        System.out.println ("Skipping whitelist item: " + wlItem);
                                //}
                        }

                        String stra[] = (String[])onlyCSV.toArray(new String[0]);
                        Arrays.sort(stra);
                        fReturn = (fReturn && onlyCSV.size() == 0);
                        System.out.println ("-------------------------");
                        System.out.println("Only in CSV: " + onlyCSV.size() + " files.");
                        System.out.println ("-------------------------");
                        if (onlyCSV.size() > 0)
                        {
                                System.out.println ("\t" + printStrArr(stra, "\n\t"));
                                if (_debug) {
                                        System.out.println(onlyCSV);
                                }
                        }
                        else {
                                System.out.println ("\t Test passed successfully :)");
                        }

                        //Destroys setMVN, but we do not need it any more.
                        setMVN.removeAll(dataSet);
                        //Remove whitelisted items
                        for (String wlItem : white) {
                                if (setMVN.contains(wlItem)) {
                                        setMVN.remove(wlItem);
                                        System.out.println ("* Ignoring mvn whitelist item: " + wlItem);
                                }
                                //else {
                                //        System.out.println ("Skipping whitelist item: " + wlItem);
                                //}
                        }

                        fReturn = (fReturn && setMVN.size() == 0);
                        stra = (String[])setMVN.toArray(new String[0]);
                        Arrays.sort(stra);
                        System.out.println ("\n-------------------------");
                        System.out.println("Only in Maven output: " + setMVN.size() + " files.");
                        System.out.println ("-------------------------");
                        if (setMVN.size() > 0)
                        {
                                System.out.println ("\t" + printStrArr(stra, "\n\t"));
                                if (_debug) {
                                        System.out.println(setMVN);
                                }
                        }
                        else {
                                System.out.println ("\t Test passed successfully :)");
                        }
                }
                return fReturn;
        }

        public static void main(String[] args)
        {
                boolean fOK = true;

                try
                {
                        CLIParse parser = new CLIParse(args);
                        if (args.length == 0 || parser.hasArg("?") || parser.hasArg("help"))
                        {
                                printUsage();
                                System.exit(0);
                        }

                        if (!validate(parser))
                        {
                                System.exit(1);
                        }

                        ManifestUtil mu = new ManifestUtil(parser.getArg("csv"), parser.getArg("ear"), parser.getArg("root"), parser.getArg("wlist"), parser.getArg("repo"), parser.hasArg("debug"));

                        HashSet dataSet = mu.getDataSet();

                        fOK = (dataSet.size()>0);
                        if (fOK)
                        {
                                System.out.println ("\n================================");
                                System.out.println ("Testing CSV vs. jar files in EAR");
                                System.out.println ("================================");
                                fOK = mu.testFiles(dataSet);
                                System.out.println ("\n*** CSV vs. EAR results: " + (fOK ? "Success" : "Failure") + " ***");

                                System.out.println ("\n============= *** ==============");

                                System.out.println ("\n================================");
                                System.out.println ("Testing CSV vs. Maven dependency");
                                System.out.println ("================================");
                                boolean fMvnOK = mu.testMVN(dataSet);
                                System.out.println ("\n*** CSV vs. Maven results: " + (fMvnOK ? "Success" : "Failure") + " ***");
                                
                                fOK = (fOK && fMvnOK);
                                System.out.println ("\n============== *** ===============");
                                System.out.println ("*** Final test results: " + (fOK ? "Success" : "Failure") + " ***");
                                System.out.println ("============== *** ===============");
                        }
                }
                catch (Exception e)
                {
                        System.out.println ("ManifestUtil failed!\nError details:" + e.getMessage());
                        e.printStackTrace();
                        fOK = false;
                }

                System.exit(fOK ? 0 : 1);
        }
}
