/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
package helper;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

import share.DescEntry;
import share.DescGetter;

/**
 * This is the Office-API specific DescGetter<br>
 * <br>
 * Examples:<br><br>
 * -o sw.SwXBodyText<br>
 * runs the module test of <B>Sw.SwXBodyText</B><br>
 * <br>
 * -o sw.SwXBodyText::com::sun::star::text::Text<br>
 * runs only the interface test <B>com.sun.star.textText</B> of the module <B>Sw.SwXBodyText</B><br>
 * <br>
 * -o sw.SwXBodyText::com::sun::star::text::Text,com::sun::star::text::XSimpleText<br>
 * runs only the interfaces test <B>com.sun.star.textText</B> and <B>com.sun.star.text.XSimpleText</B> of the module <B>Sw.SwXBodyText</B><br>
 * <br>
 * -p sw<br>
 * runs all modules of the project <B>sw</B><br>
 * <br>
 * -p listall<br>
 * lists all known module tests<br>
 * <br>
 * -sce SCENARIO_FILE<br>
 * A scenario file is a property file which could cotain <B>-o</B> and <B>-p</B> properties<br>
 * <br>
 * -sce sw.SwXBodyText,sw.SwXBookmark<br>
 * runs the module test of <B>Sw.SwXBodyText</B> and <B>sw.SwXBookmark</B><br>
 */
public class APIDescGetter extends DescGetter
{

    private static String fullJob = null;

    /*
     * gets the needed information about a StarOffice component
     * @param descPath Path to the ComponentDescription
     * @param entry contains the entry name, e.g. sw.SwXBodyText
     * @param debug if true some debug information is displayed on standard out
     */
    public DescEntry[] getDescriptionFor(String job, String descPath,
            boolean debug)
    {

        if (job.startsWith("-o"))
        {
            job = job.substring(3, job.length()).trim();

            if (job.indexOf(".") < 0)
            {
                return null;
            }

            // special in case several Interfaces are given comma separated
            if (job.indexOf(",") < 0)
            {
                DescEntry entry = getDescriptionForSingleJob(job, descPath,
                        debug);

                if (entry != null)
                {
                    return new DescEntry[]
                            {
                                entry
                            };
                }
                else
                {
                    return null;
                }
            }
            else
            {
                ArrayList subs = getSubInterfaces(job);
                String partjob = job.substring(0, job.indexOf(",")).trim();
                DescEntry entry = getDescriptionForSingleJob(partjob, descPath,
                        debug);

                if (entry != null)
                {
                    for (int i = 0; i < entry.SubEntryCount; i++)
                    {
                        String subEntry = entry.SubEntries[i].longName;
                        int cpLength = entry.longName.length();
                        subEntry = subEntry.substring(cpLength + 2,
                                subEntry.length());

                        if (subs.contains(subEntry))
                        {
                            entry.SubEntries[i].isToTest = true;
                        }
                    }

                    return new DescEntry[]
                            {
                                entry
                            };
                }
                else
                {
                    return null;
                }
            }
        }

        if (job.startsWith("-p"))
        {
            job = job.substring(3, job.length()).trim();

            String[] scenario = createScenario(descPath, job, debug);
            if (scenario == null)
            {
                return null;
            }
            DescEntry[] entries = new DescEntry[scenario.length];
            for (int i = 0; i < scenario.length; i++)
            {
                entries[i] = getDescriptionForSingleJob(
                        scenario[i].substring(3).trim(), descPath, debug);
            }
            if (job.equals("listall"))
            {
                util.dbg.printArray(scenario);
                System.exit(0);
            }
            return entries;
        }

        if (job.startsWith("-sce"))
        {
            job = job.substring(5, job.length()).trim();

            File sceFile = new File(job);
            if (sceFile.exists())
            {
                return getScenario(job, descPath, debug);
            }
            else
            {
                //look the scenarion like this? :
                // sw.SwXBodyText,sw.SwXTextCursor
                ArrayList subs = getSubObjects(job);
                DescEntry[] entries = new DescEntry[subs.size()];

                for (int i = 0; i < subs.size(); i++)
                {
                    entries[i] = getDescriptionForSingleJob(
                            (String) subs.get(i), descPath, debug);
                }
                return entries;
            }
        }
        else
        {
            return null;
        }
    }

    protected DescEntry getDescriptionForSingleJob(String job, String descPath,
            boolean debug)
    {
        boolean isSingleInterface = job.indexOf("::") > 0;
        fullJob = job;

        if (isSingleInterface)
        {
            job = job.substring(0, job.indexOf("::"));
        }

        if (job.startsWith("bugs"))
        {
            DescEntry Entry = new DescEntry();
            Entry.entryName = job;
            Entry.longName = job;
            Entry.EntryType = "bugdoc";
            Entry.isOptional = false;
            Entry.isToTest = true;
            Entry.SubEntryCount = 0;
            Entry.hasErrorMsg = false;
            Entry.State = "non possible";

            return Entry;
        }

        DescEntry entry = null;

        if (descPath != null)
        {
            if (debug)
            {
                System.out.println("## reading from File " + descPath);
            }

            entry = getFromDirectory(descPath, job, debug);
        }
        else
        {
            if (debug)
            {
                System.out.println("## reading from jar");
            }

            entry = getFromClassPath(job, debug);
        }

        boolean foundInterface = false;

        if (isSingleInterface && (entry != null))
        {
            for (int i = 0; i < entry.SubEntryCount; i++)
            {
                if (!(entry.SubEntries[i].longName).equals(fullJob))
                {
                    entry.SubEntries[i].isToTest = false;
                }
                else
                {
                    foundInterface = true;
                    entry.SubEntries[i].isToTest = true;
                }
            }
        }

        if (isSingleInterface && !foundInterface || entry == null)
        {
            return setErrorDescription(entry,
                    "couldn't find a description for test '" + fullJob + "'");
        }

        return entry;
    }

    protected static DescEntry[] getSubEntries(BufferedReader cvsFile,
            DescEntry parent, boolean debug)
    {
        String line = "";
        String old_ifc_name = "";
        ArrayList ifc_names = new ArrayList();
        ArrayList meth_names = new ArrayList();
        DescEntry ifcDesc = null;

        while (line != null)
        {
            try
            {
                line = cvsFile.readLine();
                if (line == null)
                {
                    continue;
                }
                if (line.startsWith("#"))
                {
                    continue;
                }
                if (line.length() <= 0)
                {
                    continue;
                }
// TODO Probleme here
                // int nFirstSemicolon = line.indexOf(";");
                // int nLastSemicolon = line.lastIndexOf(";");

                String unknown;
                String ifc_name = ""; //  = line.substring(line.indexOf(";") + 2, line.lastIndexOf(";") - 1);
                String meth_name = ""; //  = line.substring(line.lastIndexOf(";") + 2, line.length() - 1);
                StringTokenizer aToken = new StringTokenizer(line, ";");
                if (aToken.countTokens() < 3)
                {
                    System.out.println("Wrong format: Line '" + line + "' is not supported.");
                    continue;
                }
                if (aToken.hasMoreTokens())
                {
                    unknown = StringHelper.removeQuoteIfExists(aToken.nextToken());
                }
                if (aToken.hasMoreTokens())
                {
                    ifc_name = StringHelper.removeQuoteIfExists(aToken.nextToken());
                }
                if (aToken.hasMoreTokens())
                {
                    meth_name = StringHelper.removeQuoteIfExists(aToken.nextToken());
                }

                // String ifc_name = line.substring(line.indexOf(";") + 2, line.lastIndexOf(";") - 1);
                // String meth_name = line.substring(line.lastIndexOf(";") + 2, line.length() - 1);

                DescEntry methDesc = createDescEntry(meth_name, ifc_name, parent);

                if (!ifc_name.equals(old_ifc_name))
                {
                    if (ifcDesc != null)
                    {
                        ifcDesc.SubEntries = getDescArray(meth_names.toArray());
                        ifcDesc.SubEntryCount = meth_names.size();

                        //mark service/interface as optional if all methods/properties are optional
                        boolean allOptional = true;

                        for (int k = 0; k < ifcDesc.SubEntryCount; k++)
                        {
                            if (!ifcDesc.SubEntries[k].isOptional)
                            {
                                allOptional = false;
                            }
                        }

                        if (!ifcDesc.isOptional && allOptional)
                        {
                            ifcDesc.isOptional = allOptional;
                        }

                        meth_names.clear();
                        ifc_names.add(ifcDesc);
                    }

                    ifcDesc = new DescEntry();
                    ifcDesc.isToTest = true;
                    old_ifc_name = ifc_name;

                    if (ifc_name.indexOf("#optional") > 0)
                    {
                        ifcDesc.isOptional = true;
                        ifc_name = ifc_name.substring(0, ifc_name.indexOf("#"));
                    }

                    String className = createClassName(ifc_name);

                    ifcDesc.EntryType = entryType;
                    ifcDesc.entryName = "ifc" + className;
                    ifcDesc.longName = parent.entryName + "::" + ifc_name;
                }
                meth_names.add(methDesc);

            }
            catch (java.io.IOException ioe)
            {
                parent.hasErrorMsg = true;
                parent.ErrorMsg = "IOException while reading the description";

                return null;
            }
        }

        ifcDesc.SubEntries = getDescArray(meth_names.toArray());
        ifcDesc.SubEntryCount = meth_names.size();

        //mark service/interface as optional if all methods/properties are optional
        boolean allOptional = true;

        for (int k = 0; k < ifcDesc.SubEntryCount; k++)
        {
            if (!ifcDesc.SubEntries[k].isOptional)
            {
                allOptional = false;
            }
        }

        if (!ifcDesc.isOptional && allOptional)
        {
            ifcDesc.isOptional = allOptional;
        }

        ifc_names.add(ifcDesc);

        return getDescArray(makeArray(ifc_names));
    }
    private static String createClassName(String _ifc_name)
    {
        StringTokenizer st = new StringTokenizer(_ifc_name, ":");
        String className = "";

        int count = 3;

        if (_ifc_name.startsWith("drafts"))
        {
            count = 4;
        }

        for (int i = 0; st.hasMoreTokens(); i++)
        {
            String token = st.nextToken();

            // skipping (drafts.)com.sun.star
            if (i >= count)
            {
                if (!st.hasMoreTokens())
                {
                    // inserting '_' before the last token
                    token = "_" + token;
                }

                className += ("." + token);
            }
        }
        return className;
    }

    private static String entryType;

    private static DescEntry createDescEntry(String meth_name, String ifc_name, DescEntry parent)
    {
        entryType = "service";
        DescEntry methDesc = new DescEntry();

        if (meth_name.indexOf("#optional") > 0)
        {
            methDesc.isOptional = true;
            meth_name = meth_name.substring(0, meth_name.indexOf("#"));
        }

        if (meth_name.endsWith("()"))
        {
            methDesc.EntryType = "method";
            entryType = "interface";
        }
        else
        {
            methDesc.EntryType = "property";
            entryType = "service";
        }

        methDesc.entryName = meth_name;
        methDesc.isToTest = true;


        String withoutHash = ifc_name;

        if (ifc_name.indexOf("#optional") > 0)
        {
            withoutHash = ifc_name.substring(0, ifc_name.indexOf("#"));
        }

        methDesc.longName = parent.entryName + "::" + withoutHash + "::" + meth_name;

        return methDesc;
    }

    private static void createIfcName(String ifc_name, ArrayList meth_names, DescEntry ifcDesc)
    {
    }

    /**
     * This method ensures that XComponent will be the last in the list of interfaces
     */
    protected static Object[] makeArray(ArrayList entries)
    {
        Object[] entriesArray = entries.toArray();
        ArrayList returnArray = new ArrayList();
        Object addAtEnd = null;

        for (int k = 0; k < entriesArray.length; k++)
        {
            DescEntry entry = (DescEntry) entriesArray[k];

            if (entry.entryName.equals("ifc.lang._XComponent"))
            {
                addAtEnd = entry;
            }
            else
            {
                returnArray.add(entry);
            }
        }

        if (addAtEnd != null)
        {
            returnArray.add(addAtEnd);
        }

        return returnArray.toArray();
    }

    protected static DescEntry setErrorDescription(DescEntry entry,
            String ErrorMsg)
    {
        if (entry == null)
        {
            entry = new DescEntry();
        }
        entry.hasErrorMsg = true;
        entry.ErrorMsg = "Error while getting description for test '" +
                fullJob + "' as an API test: " + ErrorMsg;

        return entry;
    }

    protected static DescEntry[] getDescArray(Object[] list)
    {
        DescEntry[] entries = new DescEntry[list.length];

        for (int i = 0; i < list.length; i++)
        {
            entries[i] = (DescEntry) list[i];
        }

        return entries;
    }

    protected DescEntry getFromClassPath(String aEntry, boolean debug)
    {
        int dotindex = aEntry.indexOf('.');

        if (dotindex == -1)
        {
            return null;
        }

        String module = null;
        String shortName = null;

        if (aEntry.indexOf(".uno") == -1)
        {
            module = aEntry.substring(0, aEntry.indexOf('.'));
            shortName = aEntry.substring(aEntry.indexOf('.') + 1);
        }
        else
        {
            module = aEntry.substring(0, aEntry.lastIndexOf('.'));
            shortName = aEntry.substring(aEntry.lastIndexOf('.') + 1);
        }

        DescEntry theEntry = new DescEntry();
        theEntry.entryName = aEntry;
        theEntry.longName = aEntry;
        theEntry.isOptional = false;
        theEntry.EntryType = "component";
        theEntry.isToTest = true;

        BufferedReader csvFile = null;

        java.net.URL url = this.getClass().getResource("/objdsc/" + module);

        if (url == null)
        {
            return setErrorDescription(theEntry,
                    "couldn't find module '" + module + "'");
        }

        try
        {
            java.net.URLConnection con = url.openConnection();

            String sEndsWithCSVName = "." + shortName.trim() + ".csv";
            if (con instanceof java.net.JarURLConnection)
            {
                // get Jar file from connection
                java.util.jar.JarFile f = ((java.net.JarURLConnection) con).getJarFile();

                // Enumerate over all entries
                java.util.Enumeration e = f.entries();

                String sStartModule = "/" + module + "/";
                while (e.hasMoreElements())
                {

                    String entry = e.nextElement().toString();

//                    if (debug) {
//                        System.out.println("### Read from connetion: " + entry);
//                    }

                    if ((entry.lastIndexOf(sStartModule) != -1) &&
                            entry.endsWith(sEndsWithCSVName))
                    {
                        InputStream input = this.getClass().getResourceAsStream("/" + entry);
                        csvFile = new BufferedReader(new InputStreamReader(input));
                        break;
                    }
                }
            }
            else
            {
                InputStream in = con.getInputStream();
                java.io.BufferedReader buf = new java.io.BufferedReader(new InputStreamReader(in));
                boolean found = false;

                while (buf.ready() && !found)
                {
                    String entry = buf.readLine();

                    if (entry.endsWith(sEndsWithCSVName))
                    {
                        System.out.println("FOUND  ####");
                        InputStream input = this.getClass().getResourceAsStream("/objdsc/" +
                                module +
                                "/" +
                                entry);
                        csvFile = new BufferedReader(
                                new InputStreamReader(input));
                        found = true;
                    }
                }

                buf.close();
            }
        }
        catch (java.io.IOException e)
        {
            e.printStackTrace();
        }

        if (csvFile == null)
        {
            return setErrorDescription(theEntry,
                    "couldn't find component '" +
                    theEntry.entryName + "'");
        }

        DescEntry[] subEntries = getSubEntries(csvFile, theEntry, debug);

        theEntry.SubEntryCount = subEntries.length;
        theEntry.SubEntries = subEntries;

        return theEntry;
    }

    protected static DescEntry getFromDirectory(String descPath, String entry,
            boolean debug)
    {
        int dotindex = entry.indexOf('.');

        if (dotindex == -1)
        {
            return null;
        }

        String fs = System.getProperty("file.separator");
        String module = null;
        String shortName = null;

        if (entry.indexOf(".uno") == -1)
        {
            module = entry.substring(0, entry.indexOf('.'));
            shortName = entry.substring(entry.indexOf('.') + 1);
        }
        else
        {
            module = entry.substring(0, entry.lastIndexOf('.'));
            shortName = entry.substring(entry.lastIndexOf('.') + 1);
        }

        DescEntry aEntry = new DescEntry();
        aEntry.entryName = entry;
        aEntry.longName = entry;
        aEntry.isOptional = false;
        aEntry.EntryType = "component";
        aEntry.isToTest = true;

        if (debug)
        {
            System.out.println("Parsing Description Path: " + descPath);
            System.out.println("Searching module: " + module);
            System.out.println("For the Component " + shortName);
        }

        File modPath = new File(descPath + fs + module);

        if (!modPath.exists())
        {
            return setErrorDescription(aEntry,
                    "couldn't find module '" + module + "'");
        }

        String[] files = modPath.list();
        String found = "none";

        for (int i = 0; i < files.length; i++)
        {
            if (files[i].endsWith("." + shortName + ".csv"))
            {
                found = files[i];
                System.out.println("found " + found);
                break;
            }
        }

        if (found.equals("none"))
        {
            return setErrorDescription(aEntry,
                    "couldn't find component '" + entry + "'");
        }

        String aUrl = descPath + fs + module + fs + found;

        BufferedReader csvFile = null;

        try
        {
            csvFile = new BufferedReader(new FileReader(aUrl));
        }
        catch (java.io.FileNotFoundException fnfe)
        {
            return setErrorDescription(aEntry, "couldn't find file '" + aUrl + "'");
        }

        DescEntry[] subEntries = getSubEntries(csvFile, aEntry, debug);

        aEntry.SubEntryCount = subEntries.length;
        aEntry.SubEntries = subEntries;

        return aEntry;
    }

    protected ArrayList getSubInterfaces(String job)
    {
        ArrayList namesList = new ArrayList();
        StringTokenizer st = new StringTokenizer(job, ",");

        for (int i = 0; st.hasMoreTokens(); i++)
        {
            String token = st.nextToken();

            if (token.indexOf(".") < 0)
            {
                namesList.add(token);
            }
        }

        return namesList;
    }

    protected ArrayList getSubObjects(String job)
    {
        ArrayList namesList = new ArrayList();
        StringTokenizer st = new StringTokenizer(job, ",");

        for (int i = 0; st.hasMoreTokens(); i++)
        {
            namesList.add(st.nextToken());
        }

        return namesList;
    }

    protected String[] createScenario(String descPath, String job,
            boolean debug)
    {
        String[] scenario = null;

        if (descPath != null)
        {
            if (debug)
            {
                System.out.println("## reading from File " + descPath);
            }

            scenario = getScenarioFromDirectory(descPath, job, debug);
        }
        else
        {
            if (debug)
            {
                System.out.println("## reading from jar");
            }

            scenario = getScenarioFromClassPath(job, debug);
        }

        return scenario;
    }

    protected String[] getScenarioFromDirectory(String descPath, String job,
            boolean debug)
    {
        String[] modules = null;
        ArrayList componentList = new ArrayList();

        if (!job.equals("unknown") && !job.equals("listall"))
        {
            modules = new String[]
                    {
                        job
                    };
        }
        else
        {
            File dirs = new File(descPath);

            if (!dirs.exists())
            {
                modules = null;
            }
            else
            {
                modules = dirs.list();
            }
        }

        for (int i = 0; i < modules.length; i++)
        {
            if (!isUnusedModule(modules[i]))
            {
                File moduleDir = new File(descPath + System.getProperty("file.separator") + modules[i]);
                if (moduleDir.exists())
                {
                    String[] components = moduleDir.list();
                    for (int j = 0; j < components.length; j++)
                    {
                        if (components[j].endsWith(".csv"))
                        {
                            String toAdd = getComponentForString(components[j], modules[i]);
                            toAdd = "-o " + modules[i] + "." + toAdd;
                            componentList.add(toAdd);
                        }
                    }
                }
            }
        }

        String[] scenario = new String[componentList.size()];
        Collections.sort(componentList);

        for (int i = 0; i < componentList.size(); i++)
        {
            scenario[i] = (String) componentList.get(i);
        }

        return scenario;

    }

    protected String[] getScenarioFromClassPath(String job, boolean debug)
    {
        String subdir = "/";

        if (!job.equals("unknown") && !job.equals("listall"))
        {
            subdir += job;
        }

        java.net.URL url = this.getClass().getResource("/objdsc" + subdir);

        if (url == null)
        {
            return null;
        }

        ArrayList scenarioList = new ArrayList();

        try
        {
            java.net.URLConnection con = url.openConnection();

            if (con instanceof java.net.JarURLConnection)
            {
                // get Jar file from connection
                java.util.jar.JarFile f = ((java.net.JarURLConnection) con).getJarFile();

                // Enumerate over all entries
                java.util.Enumeration e = f.entries();

                while (e.hasMoreElements())
                {
                    String entry = e.nextElement().toString();

                    if (entry.startsWith("objdsc" + subdir) &&
                            (entry.indexOf("CVS") < 0) &&
                            !entry.endsWith("/"))
                    {
                        int startMod = entry.indexOf("/");
                        int endMod = entry.lastIndexOf("/");
                        String module = entry.substring(startMod + 1, endMod);
                        String component = getComponentForString(
                                entry.substring(endMod + 1,
                                entry.length()),
                                module);

                        if (!isUnusedModule(module))
                        {
                            scenarioList.add("-o " + module + "." +
                                    component);
                        }
                    }
                }
            }
        }
        catch (java.io.IOException e)
        {
            e.printStackTrace();
        }

        String[] scenario = new String[scenarioList.size()];
        Collections.sort(scenarioList);

        for (int i = 0; i < scenarioList.size(); i++)
        {
            scenario[i] = (String) scenarioList.get(i);
        }

        return scenario;
    }

    protected String getComponentForString(String full, String module)
    {
        String component = "";


        //cutting .csv
        full = full.substring(0, full.length() - 4);

        //cutting component
        int lastdot = full.lastIndexOf(".");
        component = full.substring(lastdot + 1, full.length());

        if (module.equals("file") || module.equals("xmloff"))
        {
            String withoutComponent = full.substring(0, lastdot);
            int preLastDot = withoutComponent.lastIndexOf(".");
            component = withoutComponent.substring(preLastDot + 1,
                    withoutComponent.length()) +
                    "." + component;
        }

        return component;
    }

    protected boolean isUnusedModule(String moduleName)
    {
        ArrayList removed = new ArrayList();
        removed.add("acceptor");
        removed.add("brdgfctr");
        removed.add("connectr");
        removed.add("corefl");
        removed.add("cpld");
        removed.add("defreg");
        removed.add("dynamicloader");
        removed.add("impreg");
        removed.add("insp");
        removed.add("inv");
        removed.add("invadp");
        removed.add("javaloader");
        removed.add("jen");
        removed.add("namingservice");
        removed.add("proxyfac");
        removed.add("rdbtdp");
        removed.add("remotebridge");
        removed.add("simreg");
        removed.add("smgr");
        removed.add("stm");
        removed.add("tcv");
        removed.add("tdmgr");
        removed.add("ucprmt");
        removed.add("uuresolver");

        return removed.contains(moduleName);
    }
}
