/*
 * Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved.
 * This component and the accompanying materials are made available
 * under the terms of the License "Eclipse Public License v1.0"
 * which accompanies this distribution, and is available
 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
 *
 * Initial Contributors:
 * Nokia Corporation - initial contribution.
 *
 * Contributors:
 *
 * Description: 
 *
 */

package com.nokia.ant.taskdefs;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * Outputs a directed graph of Ant library dependencies, reads information from dependency jars
 */
public class AntDependencyTask extends Task {
    private ArrayList<FileSet> antFileSetList = new ArrayList<FileSet>();
    private String outputFile;

    public AntDependencyTask() {
        setTaskName("AntDependencyTask");
    }

    /**
     * Add a set of files to copy.
     * 
     * @param set a set of files to AntDependencyTask.
     * @ant.required
     */
    public void addFileset(FileSet set) {
        antFileSetList.add(set);
    }

    /**
     * Location of graph file to output to
     * 
     * @ant.required
     */
    public void setOutputFile(String path) {
        outputFile = path;
    }

    public String classToJar(Class aclass) {
        String name = aclass.getName().replace(".", "/") + ".class";

        for (Iterator iterator = antFileSetList.iterator(); iterator.hasNext();) {
            FileSet fs = (FileSet) iterator.next();
            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
            String[] srcFiles = ds.getIncludedFiles();
            String basedir = ds.getBasedir().getPath();
            // log(basedir);

            for (int i = 0; i < srcFiles.length; i++) {
                String fileName = basedir + File.separator + srcFiles[i];
                // log(fileName);
                try {
                    JarFile jar = new JarFile(fileName);

                    // for (Enumeration e = jar.entries(); e.hasMoreElements() ;)
                    // {log(e.nextElement().toString()); }

                    if (jar.getJarEntry(name) != null) {
                        return fileName;
                    }
                }
                catch (IOException e) {
                    // We are Ignoring the errors as no need to fail the build.
                    log(e.getMessage(), Project.MSG_DEBUG);
                }
            }
        }
        log(name + " not found", Project.MSG_DEBUG);
        return null;
    }

    public String getJarAttr(JarFile jar, String nameOfAttr) {
        try {
            String attr = jar.getManifest().getMainAttributes().getValue(nameOfAttr);
            if (attr != null) {
                return attr;
            }

            Manifest manifest = jar.getManifest();
            Map map = manifest.getEntries();

            for (Iterator it = map.keySet().iterator(); it.hasNext();) {
                String entryName = (String) it.next();
                Attributes attrs = (Attributes) map.get(entryName);

                for (Iterator it2 = attrs.keySet().iterator(); it2.hasNext();) {
                    Attributes.Name attrName = (Attributes.Name) it2.next();
                    if (attrName.toString() == nameOfAttr) {
                        return attrs.getValue(attrName).replace("\"", "");
                    }
                }
            }
        }
        catch (IOException e) {
            // We are Ignoring the errors as no need to fail the build.
            log("Not able to get the JAR file attribute information. " + e.getMessage(), Project.MSG_DEBUG);
        }
        return null;
    }

    public HashSet<String> getJarInfo() {
        HashSet<String> classlist = new HashSet<String>();

        for (Iterator iterator = antFileSetList.iterator(); iterator.hasNext();) {
            FileSet fs = (FileSet) iterator.next();
            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
            String[] srcFiles = ds.getIncludedFiles();
            String basedir = ds.getBasedir().getPath();
            // log(basedir);

            for (int i = 0; i < srcFiles.length; i++) {
                String fileName = basedir + File.separator + srcFiles[i];
                // log(fileName);
                try {
                    JarFile jar = new JarFile(fileName);

                    String vendor = getJarAttr(jar, "Implementation-Vendor");
                    String version = getJarAttr(jar, "Implementation-Version");
                    if (version == null) {
                        version = getJarAttr(jar, "Specification-Version");
                    }
                    String name = convertJarName(fileName);

                    // findLicense(srcFiles[i], jar);

                    String nameandversion = name;

                    if (version != null) {
                        version = version.replace("$", "");
                        if (!digitInString(name)) {
                            nameandversion = name + " " + version;
                        }
                    }
                    if (vendor == null) {
                        vendor = "";
                    }
                    classlist.add(name + " [style=filled,shape=record,label=\"" + nameandversion
                        + "|" + vendor + "\"];");
                }
                catch (IOException e) {
                    // We are Ignoring the errors as no need to fail the build.
                    e.printStackTrace();
                }
            }
        }

        return classlist;
    }

    public void findLicense(String name, JarFile jar) {
        try {
            ZipEntry entry = jar.getEntry("META-INF/LICENSE");
            if (entry == null) {
                entry = jar.getEntry("META-INF/LICENSE.txt");
            }
            if (entry != null) {
                /**/
                log("File in " + name + " in jar file ", Project.MSG_DEBUG);
                byte[] data = new byte[1024];
                jar.getInputStream(entry).read(data);
                for (String line : new String(data).split("\n")) {
                    if (line.contains("License") || line.contains("LICENSE ")
                        || line.contains("Copyright")) {
                        log("Replace License information with * " + line.replace("*", "").trim(), Project.MSG_INFO);
                        break;
                    }
                }
            }
            else {
                // http://mirrors.ibiblio.org/pub/mirrors/maven2/
                String mavenUrl = "http://repo2.maven.org/maven2/";
                Enumeration jarfiles = jar.entries();
                boolean found = false;
                while (!found && jarfiles.hasMoreElements()) {
                    ZipEntry file = (ZipEntry) jarfiles.nextElement();
                    if (file.isDirectory()) {
                        String filename = file.getName();
                        String[] split = file.getName().split("/");
                        String end = split[split.length - 1];
                        String specialfilename = filename + end;

                        URL url = new URL(mavenUrl + filename + end + "/maven-metadata.xml");
                        if (!end.equals("apache")) {
                            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
                                filename = filename.replace(end, name.replace(".jar", ""));
                                end = name.replace(".jar", "");
                                specialfilename = filename;
                                url = new URL(mavenUrl + filename + "maven-metadata.xml");
                                connection = (HttpURLConnection) url.openConnection();
                            }
                            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {

                                SAXReader xmlReader = new SAXReader();
                                Document antDoc = xmlReader.read(url.openStream());
                                List versions = antDoc.selectNodes("//versioning/versions/version");
                                // if (version.equals(""))
                                // {
                                // version = antDoc.valueOf("/metadata/version");
                                // }
                                Collections.reverse(versions);
                                for (Object tmpversion : versions) {
                                    String version = ((Element) tmpversion).getText();
                                    URL url2 = new URL(mavenUrl + specialfilename + "/" + version
                                        + "/" + end + "-" + version + ".pom");
                                    HttpURLConnection connection2 = (HttpURLConnection) url2.openConnection();
                                    if (connection2.getResponseCode() == HttpURLConnection.HTTP_OK) {
                                        BufferedReader din = new BufferedReader(new InputStreamReader(url2.openStream()));
                                        StringBuffer sb = new StringBuffer();
                                        String line = null;
                                        while ((line = din.readLine()) != null) {
                                            line = line.replace("xmlns=\"http://maven.apache.org/POM/4.0.0\"", "");
                                            sb.append(line + "\n");
                                        }
                                        xmlReader = new SAXReader();
                                        //
                                        Document antDoc2 = xmlReader.read(new ByteArrayInputStream(new String(sb).getBytes()));
                                        String license = antDoc2.valueOf("/project/licenses/license/name");
                                        if (!license.equals("")) {
                                            found = true;
                                            break;
                                        }

                                    }
                                }
                            }
                        }
                    }

                }
                if (!found) {
                    log(name + " not found in " + jar, Project.MSG_INFO);
                }
            }
        }
        catch (IOException e) {
            // We are Ignoring the errors as no need to fail the build.
            e.printStackTrace();
        }
        catch (DocumentException e) {
            // We are Ignoring the errors as no need to fail the build.
            e.printStackTrace();
        }
    }

    public boolean digitInString(String s) {
        int j = s.length() - 1;
        while (j >= 0 && Character.isDigit(s.charAt(j))) {
            return true;
        }
        return false;
    }

    public String convertJarName(String jar) {
        return new File(jar).getName().replace(".jar", "").replace("-", "_").replace(".", "_");
    }

    public final void execute() {
        try {
            Project project = getProject();

            Hashtable taskdefs = project.getTaskDefinitions();

            HashSet<String> classlist = new HashSet<String>();

            Enumeration taskdefsenum = taskdefs.keys();
            while (taskdefsenum.hasMoreElements()) {
                String key = (String) taskdefsenum.nextElement();
                Class value = (Class) taskdefs.get(key);
                if (!key.contains("nokia") && !value.toString().contains("org.apache.tools.ant")) {
                    String name = value.getPackage().getName();
                    String vendor = value.getPackage().getImplementationVendor();

                    name = classToJar(value);

                    if (name != null) {
                        name = convertJarName(name);

                        classlist.add("helium_ant -> \"" + name + "\";");

                        if (vendor == null) {
                            vendor = "";
                        }

                        classlist.add(name + " [style=filled,shape=record,label=\"" + name + "|"
                            + vendor + "\"];");
                    }
                }
            }

            classlist.add("helium_ant -> nokia_ant;");

            classlist.addAll(getJarInfo());

            Writer output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), "UTF8"));

            for (String value : classlist) {
                output.write(value + "\n");
            }

            output.close();
        }
        catch (FileNotFoundException e) {
            // We are Ignoring the errors as no need to fail the build.
            log("FileNotFoundException occured while getting the ANT task dependency information. "
                + e.getMessage(), Project.MSG_DEBUG);
        }
        catch (UnsupportedEncodingException e) {
            // We are Ignoring the errors as no need to fail the build.
            log("UnsupportedEncodingException occured while getting the ANT task dependency information. "
                + e.getMessage(), Project.MSG_DEBUG);
        }
        catch (IOException e) {
            // We are Ignoring the errors as no need to fail the build.
            log("IOException occured while getting the ANT task dependency information. "
                + e.getMessage(), Project.MSG_DEBUG);
        }
    }

}