/*
 * Help.java
 *
 * Created on May 12, 2006, 4:13 PM
 *
 */
package usda.weru.util;

import de.schlichtherle.truezip.file.TFile;
import java.io.IOException;
import java.net.*;
import java.util.*;
import java.util.jar.*;

import java.util.zip.*;
import javax.help.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 

/**
 * This Help was added to consolidate the help code throughout weps.  
 * The loading methods were duplicated in each main section of the 
 * application.  This Help class handles loading the helpsets and merging
 * them into a single HelpSet which is used anywhere in the application.
 * @author Joseph Levin
 */
public class Help {

    private static final Logger LOGGER = LogManager.getLogger(Help.class);
    /**
     * HelpSet object which is the root helpset.  Loaded helpsets are merged 
     * into this one.
     */
    private static HelpSet c_masterHelp;

    static {
        //c_masterHelp = new HelpSet();
    }

    /**
     * Getter method for the master HelpSet.  Even if only one sub HelpSet has 
     * been loaded this is the correct HelpSet.
     * @return Merged master HelpSet.
     */
    public static HelpSet getHelpSet() {
        if (c_masterHelp == null) {
            c_masterHelp = new HelpSet();
        }
        return c_masterHelp;
    }

    /**
     * Test method for determing if a HelpSet with a particular title has been 
     * loaded.
     * @param title Casesensitive String to search for.
     * @return True if the master helpset or one of it's sub helpsets 
     * matches the title.
     */
    public static boolean hasHelpSet(String title) {
        return hasHelpSet(c_masterHelp, title);
    }

    /**
     * Helper method for hasHelpSet
     * @param hs Helpset to search through and under.
     * @param title Casesensitive string to find.
     * @return True if the title is found in a helpset or subhelpset
     */
    private static boolean hasHelpSet(HelpSet hs, String title) {
        if (title == null) {
            return false;
        }
        if (hs.getTitle() != null && hs.getTitle().equals(title)) {
            return true;
        }
        HelpSet sub = null;
        Enumeration<HelpSet> enumSets = Caster.<Enumeration<HelpSet>>cast(hs.getHelpSets());
        while (enumSets.hasMoreElements()) {
            sub = enumSets.nextElement();
            if (hasHelpSet(sub, title)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Helper method for loadJar.
     * @param jarFilePath Path to the jar file.
     * @return True if the jar file was loaded.
     */
    public static boolean loadJar(String jarFilePath) {
        return loadJar(jarFilePath, null);
    }

    /**
     * Loads a helpset from a jar into the master HelpSet in memory.  If fileName 
     * is null then the first file ending in .hs is loaded.
     * @param jarFilePath String path to the jar file.
     * @param fileName Filename of the helpset within the jar.
     * @return True if the helpset was loaded.
     */
    public static boolean loadJar(String jarFilePath, String fileName) {
        HelpSet hs = null;
        // MEH: need to use canonical or absolute path in order for System.setProperty("user.dir" .... to function
        // need this so the -working option forks, so we can function in s WEbStart scenario
        TFile tempJarFile = new TFile(jarFilePath).getCanOrAbsFile();
        if (new TFile(jarFilePath).getCanOrAbsFile().exists() == false) {
            return false;
        }
        jarFilePath = tempJarFile.getCanOrAbsPath();
        
        String hsName = null;
        JarFile jar;
        try {
            jar = new JarFile(jarFilePath);
        } catch (IOException ex) {
            LOGGER.error("Unable to load jar.", ex);
            return false;
        }
        Enumeration<JarEntry> entries = jar.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            String entryName = entry.getName();

            if (fileName != null && fileName.length() > 0) {
                //Test for a helpset name.
                if (entryName.equals(fileName)) {
                    hsName = entryName;
                    break;
                }
            } //We just need a file ending in .hs
            else if (entryName.endsWith(".hs")) {
                hsName = entryName;
                break;
            }

        }//end of while loop

        URL url[] = getURLs("file:" + jarFilePath);
        // MEH ClassLoader cl = new URLClassLoader(url);
        // For Webstart, need to pass the context loader as parent, default is system loader which fails in webstart
        ClassLoader cl = new URLClassLoader(url,Thread.currentThread().getContextClassLoader());
        URL hsUrl = HelpSet.findHelpSet(cl, hsName);
        try {
            hs = new HelpSet(cl, hsUrl);
            addHelpSet(hs);

            return true;
        } catch (HelpSetException ex) {

            LOGGER.error("Unable to load jar.", ex);
            return false;
        }

        //Add the helpset to the master helpset    
//        } catch (Exception exc) {
//            //System.out.println("Can't find a HelpSet file in: " +jarFilePath);
//            hs = null;
//            return false;
//        }        
    }

    /**
     * Adds a helpset to the master HelpSet in memory.  If the master HelpSet 
     * is null then the passed HelpSet is assigned as the MasterHelp set.
     * @param hs HelpSet to add.
     */
    private static void addHelpSet(HelpSet hs) {
        if (c_masterHelp == null) {
            c_masterHelp = hs;
            return;
        }
        c_masterHelp.add(hs);
        @SuppressWarnings("unchecked")
        Enumeration<HelpSet> enumHS = (Enumeration<HelpSet>) hs.getHelpSets();
        while (enumHS.hasMoreElements()) {
            c_masterHelp.add(enumHS.nextElement());
        }

    }

    /**
     * This function just generate a URL from a string and then puts
     * it into a URL array
     * @param spec 
     * @return Array of URLs
     */
    private static URL[] getURLs(String spec) {
        Vector<URL> v = new Vector<>();
        URL url;
        try {
            url = new URL(spec);
        } catch (MalformedURLException ex) {
            LOGGER.debug("URL Error", ex);
            return null;
        }
        v.addElement(url);
        URL back[] = new URL[v.size()];
        v.copyInto(back);
        return back;
    }

    /**
     * Loads a directory based Helpset.
     * @param dirFilePath String path to the HelpSet.
     * @return True if the HelpSet was loaded.
     */
    public static boolean loadDirectory(String dirFilePath) {
        try {
            if (new TFile(dirFilePath).exists() == false) {
                return false;
            }
            URL hsURL = new URL((new TFile(".")).toURI().toURL(), dirFilePath);
            HelpSet hs = new HelpSet(null, hsURL);

            //Add the helpset to the master helpset    
            addHelpSet(hs);

            //System.out.println("Found help set at " + hsURL);
            return true;
        } catch (MalformedURLException | HelpSetException ee) {
            //System.err.println("HelpSet not found");
            LOGGER.error("Unexpected error", ee);
            return false;
        }
    }
}
