/*
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Build
 * (c) Copyright IBM Corporation 2016. All Rights Reserved.
 *
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.
 */
package com.urbancode.air.plugin.automation

import groovy.io.FileType;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
import java.util.regex.Pattern;

import com.urbancode.air.*
import com.urbancode.commons.util.IO


public class ScanApplication {
    def path
    def host
    def tokenPath
    def devEnvPath
    def acceptSSL

    def type
    def appName
    def scanPath
    def ozasmtFile
    def htmlFile
    CommandHelper ch

    final def out = System.out
    final def workDir = new File('.').canonicalFile

    //----------------------------------------------------------------
    public ScanApplication() {
        ch = new CommandHelper(workDir);
    }

    //----------------------------------------------------------------
    public void runScript(String file) {
        def cmdArgs = [path, 'script', file];

        println("run Script file:${file}");
        println("run Script path:${path}");
        try {
            ch.runCommand(cmdArgs.join(' '), cmdArgs);
        }
        catch (Exception e) {
            println ("Error executing script!");
            throw e;
        }
        println("Script Successfully completed");
    }

    //----------------------------------------------------------------
    public File generateGenericPPF(File file) {
        //grab the right type of templates
        String unique = System.currentTimeMillis();
        String ppfName = appName + "-" + unique;
        File ppfTemplate;
        File scanTree = new File(scanPath);
        File ppfFile = new File(scanTree, unique+".ppf");

        while (ppfFile.exists()) {
            unique = System.currentTimeMillis();
            ppfFile = new File(scanTree, unique+".ppf");
        }

        if (type.equals("ASP")) {
            ppfTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/aspbase.ppf');
        }
        else if (type.equals("dll")) {
            ppfTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/dllbase.ppf');
        }

        //create ppffile
        ppfFile.createNewFile();

        //create reader/writer
        FileReader templateStream = new FileReader(ppfTemplate);
        BufferedReader reader = new BufferedReader(templateStream);
        FileWriter fWriter = new FileWriter(ppfFile);
        BufferedWriter writer = new BufferedWriter(fWriter);
        String line;

        //copy file with replaces
        while ((line = reader.readLine()) != null) {
            line = line.replace("REPLACEME", file.getAbsolutePath());
            line = line.replace("AOEUI", ppfName);
            writer.write(line);
            writer.newLine();
        }

        writer.close();
        reader.close();

        return ppfFile;
    }

    //----------------------------------------------------------------
    public File generateJavaPPF(File file) {
        String unique = System.currentTimeMillis();
        String ppfName = appName + "-" + unique;
        File scanTree = new File(scanPath);
        File ppfTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/javabase.ppf');
        File ppfFile = new File(scanTree, unique+".ppf");

        while (ppfFile.exists()) {
            unique = System.currentTimeMillis();
            ppfFile = new File(scanTree, unique+".ppf");
        }

        //create the staging dir
        File stagingDir = new File(scanTree, unique+"_staging");
        String fullStagingPath = scanTree.getAbsolutePath() + unique+"_staging";
        String localStagingPath = ".\\" + unique+"_staging";

        stagingDir.mkdir();

        //unzip the war file using antbuilder
        def ant = new AntBuilder()   // create an antbuilder

        ant.unzip( src:file.getAbsolutePath(),
                dest:stagingDir.getAbsoluteFile(),
                overwrite:"false" )

        //create ppffile
        ppfFile.createNewFile();

        //create reader/writer
        FileReader templateStream = new FileReader(ppfTemplate);
        BufferedReader reader = new BufferedReader(templateStream);
        FileWriter fWriter = new FileWriter(ppfFile);
        BufferedWriter writer = new BufferedWriter(fWriter);
        String line;

        String localWarFile = file.getAbsolutePath().substring(new File(file.getParent()).getAbsolutePath().length()+1);
        localWarFile = ".\\" + localWarFile;


        //copy ppf file with replaces
        while ((line = reader.readLine()) != null) {
            line = line.replace("WARPATH", localWarFile);
            line = line.replace("AOEUI", ppfName);
            line = line.replace("TEMPDIR", '.');
            line = line.replace("STAGE", localStagingPath);
            line = line.replace("PRECOMPILED", scanPath);
            writer.write(line);
            writer.newLine();
        }
        reader.close();
        writer.close();

        return ppfFile;
    }

    //----------------------------------------------------------------
    public File generatePPF(File file) {
        File ppfFile;
        if (type.equals("Java")) {
            ppfFile = generateJavaPPF(file);
        }
        else {
            ppfFile = generateGenericPPF(file);
        }

        return ppfFile;
    }

    //----------------------------------------------------------------
    public File generatePAF(List<File> ppfList, String unique) {
        File scanTree = new File(scanPath);
        File pafTemplate;
        File pafFile = new File(scanTree, unique+".paf");

        if (type.equals("Java")) {
            pafTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/javabase.paf');
        }
        else if (type.equals("dll")) {
            pafTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/dllbase.paf');
        }
        else if (type.equals("ASP")) {
            pafTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/aspbase.paf');
        }
        else if (type.equals("Sln")) {
            pafTemplate = new File(System.getenv('PLUGIN_HOME')+'/templates/dotnetbase.paf');
        }

        //create reader/writer
        FileReader templateStream = new FileReader(pafTemplate);
        BufferedReader reader = new BufferedReader(templateStream);
        FileWriter fWriter = new FileWriter(pafFile);
        BufferedWriter writer = new BufferedWriter(fWriter);
        String line;

        while ((line = reader.readLine()) != null) {
            line = line.replace("AOEUI", appName);
            if (line ==~ /.*REPLACEME.*/) {
                System.out.println("adding ppfFiles")
                //add all the ppf paths to the paf file
                ppfList.each { ppfFile ->
                    System.out.println("adding new ppfFile")
                    String newLine = line;
                    newLine = newLine.replace("REPLACEME", ppfFile.getAbsolutePath());
                    writer.write(newLine);
                    writer.newLine();
                }
            }
            else {
                writer.write(line);
                writer.newLine();
            }
        }
        reader.close();
        writer.close();

        return pafFile;
    }

    //----------------------------------------------------------------
    public String generateScriptFile(List<File> ppfFiles, String xmlOut, String htmlOut) {
        String unique = System.currentTimeMillis();
        File scriptTemplate = new File(System.getenv('PLUGIN_HOME') + '/templates/ouncecli.script');
        File scanTree = new File(scanPath);
        File scriptFile = new File(scanTree, 'ounce.script');
        File pafFile = null;

        if(type.equals("sln")) {
            pafFile = setupSlnFile();
        }
        else {
            pafFile = generatePAF(ppfFiles, unique);
        }

        //create reader/writer
        scriptFile.createNewFile();
        FileReader templateStream = new FileReader(scriptTemplate);
        BufferedReader reader = new BufferedReader(templateStream);
        FileWriter fWriter = new FileWriter(scriptFile);
        BufferedWriter writer = new BufferedWriter(fWriter);
        String line;

        //copy script file with replaces
        while ((line = reader.readLine()) != null) {
            line = line.replace("PAFFILE", pafFile.getAbsolutePath());
            line = line.replace("LOGFILE", scanPath+'/'+unique+'.log');
            line = line.replace("XMLOUTPUTFILE", xmlOut);
            line = line.replace("HTMLOUTPUTFILE", new File(htmlOut).absolutePath);
            line = line.replace("@SERVER@", host);
            line = line.replace("@TOKEN_FILE@", tokenPath);
            if (acceptSSL) {
                line = line.replace("@ACCEPTSSL@", " -acceptssl");
            }
            else {
                line = line.replace("@ACCEPTSSL@", "");
            }

            writer.write(line);
            writer.newLine();
        }
        reader.close();
        writer.close();

        return scriptFile.getAbsolutePath();
    }

    //----------------------------------------------------------------
    private File setupSlnFile() {
        File slnFile = null;

        //searches the scanPath dir
        slnFile = findSlnFile();

        if (slnFile == null) {
            throw new IllegalStateException("No Sln file found for this project!");
        }
        else {
            System.out.println("Found sln file : " + slnFile.getName());
        }

        //these will all refer to the scanPath directory
        CleanSolutionFile();
        RemoveVSSFiles();
        CleanProjectFiles();

        UpgradeSolutionFile(slnFile);

        return slnFile;
    }

    //----------------------------------------------------------------
    //searches the scanPath dir for files .sln returns the File if exactly one is found in the scanPath dir
    private File findSlnFile() {
        File dir = new File(scanPath);
        File slnFile = null;

        def filePattern = ~/.*\.sln/
        def numFound = 0;

        dir.eachFileMatch(filePattern) { File fileName ->
            numFound++;
            slnFile = fileName;
        }

        if(numFound != 1) {
            slnFile = null;
        }

        return slnFile;
    }

    //----------------------------------------------------------------
    private void CleanSolutionFile() {
        String line;
        boolean skipMode = true;
        boolean modeChange = false;
        File scanTree = new File(scanPath);
        def unique = System.currentTimeMillis();

        //grab all the sln files and do the replaces necessary
        scanTree.eachFileRecurse(FileType.FILES) { File slnFile ->
            if (slnFile.getName().endsWith(".sln")) {
                System.out.println("Cleaning Sln file : " + slnFile.getName());
                File outFile = new File(slnFile.getParent(), slnFile.getName()+".bak");

                //create reader/writer
                outFile.createNewFile();
                FileReader templateStream = new FileReader(slnFile);
                BufferedReader reader = new BufferedReader(templateStream);
                FileWriter fWriter = new FileWriter(outFile);
                BufferedWriter writer = new BufferedWriter(fWriter);

                //lets do all the match and replace stuff
                while ((line = reader.readLine()) != null) {

                    if (line.matches(".*Project\\(\"\\{[a-zA-Z0-9\\-]*\\}\"\\) = \".+\", \"http.*")) {
                        line.replace("http:\\/\\/.*\\/", "");
                    }

                    if (line.matches(".*Microsoft Visual Studio Solution File, Format Version.*")) {
                        skipMode = false;
                    }

                    if (line.matches(".*GlobalSection\\(SourceCodeControl\\).*")) {
                        skipMode = true;
                    }

                    if (line.matches(".*EndGlobalSection.*") && (skipMode == true)) {
                        skipMode = false;
                        modeChange = true;
                    }

                    if ((skipMode == false) && !(line.matches(".*(SccProject|SccAux|SccLocal|SccProvider).*"))) {
                        writer.write(line);
                    }

                    if ((modeChange == true) && line.matches(".*EndGlobalSection.*")) {
                        writer.write(line);
                        modeChange = false;
                    }
                }
                reader.close();
                writer.close();

                slnFile.renameTo(new File(slnFile.getParent(), slnFile.getName() + "." + unique + ".bak"))
                outFile.renameTo(slnFile);
            }
        }
    }

    //----------------------------------------------------------------
    private void RemoveVSSFiles() {
        File scanTree = new File(scanPath);
        def badExt = [ ~/.*\.suo/, ~/.*\.vssscc/, ~/.*\.scc/, ~/.*\.vsscc/, ~/.*\.vspscc/ ]

        badExt.each { ext ->
            scanTree.eachFileMatch(ext) { File subFile ->
                subFile.delete();
                System.out.println("Deleted \"Bad\" File : " + subFile.getName());
            }

            scanTree.eachDirRecurse { subDir ->
                subDir.eachFileMatch(ext) { File subFile ->
                    subFile.delete();
                    System.out.println("Deleted \"Bad\" File : " + subFile.getName());
                }
            }
        }
    }

    //----------------------------------------------------------------
    private void CleanProjectFiles() {
        File scanTree = new File(scanPath);
        def fixExt = [ ~/.*\.csproj/, ~/.*\.vbproj/ ];

        fixExt.each { ext ->
            scanTree.eachFileMatch(ext) { File subFile ->
                CleanProjFile(subFile);
                System.out.println("Cleaned File : " + subFile.getAbsolutePath());
            }

            scanTree.eachDirRecurse { subDir ->
                subDir.eachFileMatch(ext) { File subFile ->
                    CleanProjFile(subFile);
                    System.out.println("Cleaned File : " + subFile.getAbsolutePath());
                }
            }
        }
    }

    //----------------------------------------------------------------
    private void CleanProjFile(File projFile) {
        String line;
        def unique = System.currentTimeMillis();
        File outFile = new File(projFile.getParent(), projFile.getName()+".bak");

        //create reader/writer
        outFile.createNewFile();
        FileReader templateStream = new FileReader(projFile);
        BufferedReader reader = new BufferedReader(templateStream);
        FileWriter fWriter = new FileWriter(outFile);
        BufferedWriter writer = new BufferedWriter(fWriter);

        //lets do our replace ment fun
        while ((line = reader.readLine()) != null) {
            if(line.matches(".*(SccProjectName|SccLocalPath|SccAuxPath|SccProvider).*")) {
                if (line.matches(".*UseIIS.*") && projFile.getName().matches(".*\\.vbproj.*")) {
                    line.replace("True", "False");
                }
                writer.write(line);
            }
            else {
                System.out.println("Found a bad matching line : " + line + " in " + projFile.getName());
            }
        }
        projFile.renameTo(new File(projFile.getParent(), projFile.getName() + "." + unique + ".bak"));
        outFile.renameTo(projFile);
    }

    //----------------------------------------------------------------
    private void UpgradeSolutionFile(File slnFile) {
        //TODO decide how to handle this call to devenv
        try {
            ch.runCommand("Calling devenv to upgrade sln file.", [devEnvPath, slnFile.getAbsolutePath()," /upgrade"]);
        }
        catch (Exception e) {
            throw new Exception("Error occured running devenv", e);
        }
    }

    //----------------------------------------------------------------
    public List<File> generatePPFList() {
        def filePattern;
        List<File> ppfList = new ArrayList<File>();
        if (type.equals("ASP")) {
            filePattern = ~/.*\.asp/
        }
        else if (type.equals("dll")) {
            filePattern = ~/.*\.dll/
        }
        else if (type.equals("Java")) {
            filePattern = ~/.*\.(war|ear)/
        }
        else if (type.equals("Sln")) {
            return null;
        }

        File scanTree = new File(scanPath);

        //search the source tree for all files of type asp if asp, dll if dll, war file if java
        //use eachFileMatch and eachDirRecurse to get all files
        scanTree.eachFileMatch(filePattern) { subFile ->
            ppfList.add(generatePPF(subFile));
        }

        scanTree.eachDirRecurse { subDir ->
            subDir.eachFileMatch(filePattern) { subFile ->
                ppfList.add(generatePPF(subFile));
            }
        }

        return ppfList;
    }

    //----------------------------------------------------------------
    public void runScan() {
        String unique = System.currentTimeMillis();
        String XmlOutName = ozasmtFile;
        //reportXml = XmlOutName.toString();
        String HtmlOutName = htmlFile;
        String scriptFileName = generateScriptFile(generatePPFList(), XmlOutName, HtmlOutName);
        runScript(scriptFileName);
    }
}