/*
* 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.*;
import java.util.*;

import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.DirectoryScanner;

import java.util.jar.*;
import java.util.zip.ZipEntry;
import java.net.*;
import org.dom4j.io.SAXReader;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.DocumentException;

/**
 * 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);
        }
    }

}