/**
 *#########################################################################
 *
 * A component of the Gatherer application, part of the Greenstone digital
 * library suite from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * <BR><BR>
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * <BR><BR>
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * <BR><BR>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * <BR><BR>
 *
 * This program 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 General Public License for more details.
 *
 * <BR><BR>
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *########################################################################
 */
package org.greenstone.gatherer.gui;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.tree.*;
import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.LocalGreenstone;
import org.greenstone.gatherer.file.WorkspaceTree;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.Utility;
import org.greenstone.gatherer.download.Download;
import org.greenstone.gatherer.download.DownloadScrollPane;
import org.greenstone.gatherer.download.ServerInfoDialog;
import org.greenstone.gatherer.util.XMLTools;
import org.greenstone.gatherer.cdm.*;
import org.greenstone.gatherer.gui.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import org.greenstone.gatherer.GAuthenticator;

/**
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.1
 */
public class DownloadPane
    extends JPanel {

    static final private Dimension LABEL_SIZE = new Dimension(225, 25);
    static final private Dimension TREE_SIZE = new Dimension(150, 500);
    static final private String CONTENTS[] = { "DOWNLOAD.MODE.WebDownload", "DOWNLOAD.MODE.OAIDownload", "DOWNLOAD.MODE.ZDownload" , "DOWNLOAD.MODE.SRWDownload"};

    private boolean download_button_enabled = false;
    private boolean ready = false;

    private JPanel options_pane;
    // TODO should use Vector to store all loaded downloads!!

    private DesignTree tree;
    private HashMap download_map;
    private ServerInfoDialog server_info;
    private JScrollPane list_scroll;
    private DownloadScrollPane getter;
    private String mode = null;
    private TreePath previous_path;
    private String proxy_url = "";
	    
    /** Main System code */
    public DownloadPane() {
	super();

	// TODO: Download the WDownload and the download panel fixed!!
        getter = new DownloadScrollPane();
        getter.start();
	list_scroll = getter.getDownloadJobList();

	// TODO should use Vector to store all loaded downloads!!
	String lang = Configuration.getLanguage();
	download_map = new HashMap();
	download_map.put("Web", loadDownload("WebDownload",lang));
	download_map.put("OAI", loadDownload("OAIDownload",lang));
	download_map.put("Z3950", loadDownload("Z3950Download",lang));
	download_map.put("SRW", loadDownload("SRWDownload",lang));

	// Creation
	tree = new DesignTree();
	options_pane = new JPanel();

	
	JButton clear_cache_button = new GLIButton(Dictionary.get("Mirroring.ClearCache"), Dictionary.get("Mirroring.ClearCache_Tooltip"));
	clear_cache_button.setEnabled(true);
	clear_cache_button.setMnemonic(KeyEvent.VK_C);

	JButton download_button = new GLIButton(Dictionary.get("Mirroring.Download"), Dictionary.get("Mirroring.Download_Tooltip"));
	download_button.setEnabled(true);
	download_button.setMnemonic(KeyEvent.VK_D);

	JButton information_button =  new GLIButton(Dictionary.get("Download.ServerInformation"), Dictionary.get("Download.ServerInformation_Tooltip"));
	information_button.setEnabled(true);
	information_button.setMnemonic(KeyEvent.VK_S);


       	JButton preferences_button =  new GLIButton(Dictionary.get("Mirroring.Preferences"), Dictionary.get("Mirroring.Preferences_Tooltip"));
	preferences_button.setEnabled(true);
	preferences_button.setMnemonic(KeyEvent.VK_P);

	// Connect
	clear_cache_button.addActionListener(new ClearCacheListener());
	download_button.addActionListener(new DownloadButtonListener());
	preferences_button.addActionListener(new PreferencesButtonActionListener());
	information_button.addActionListener(new InformationButtonActionListener());
	tree.addTreeSelectionListener(new TreeListener());

	// Add to Panel
	JPanel button_pane = new JPanel();
	button_pane.setLayout(new FlowLayout(FlowLayout.CENTER,20,5));
	button_pane.setBorder(BorderFactory.createEtchedBorder());
	button_pane.add(clear_cache_button);
	button_pane.add(download_button);
	button_pane.add(information_button);
	button_pane.add(preferences_button);

	JPanel tree_pane = new JPanel();
	tree_pane.setLayout(new BorderLayout());
       	tree_pane.add(new JScrollPane(tree), BorderLayout.CENTER);
	tree_pane.setPreferredSize(TREE_SIZE);
 

	Color colour_two = Configuration.getColor("coloring.collection_tree_background", false);
	options_pane.setBackground(colour_two);
	options_pane.setBorder(BorderFactory.createEtchedBorder());


	JScrollPane options_scroll_pane = new JScrollPane(options_pane);
	JSplitPane mode_pane = new JSplitPane();
	mode_pane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
	mode_pane.add(tree_pane,JSplitPane.LEFT);
	mode_pane.add(options_scroll_pane,JSplitPane.RIGHT);
	mode_pane.setDividerLocation(TREE_SIZE.width);
    
	JPanel edit_pane = new JPanel();
	edit_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2,0,0,0), BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Download Setting"), BorderFactory.createEmptyBorder(2,2,2,2))));
	edit_pane.setLayout(new BorderLayout());
	edit_pane.add(mode_pane,BorderLayout.CENTER);
	edit_pane.add(button_pane,BorderLayout.PAGE_END);

	// Add to "this"
	setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
	setLayout(new GridLayout(2,1));
	add(edit_pane);
	add(list_scroll);

	mode = "Web";
	generateOptions(options_pane,(Download)download_map.get(mode));
	previous_path = tree.getSelectionPath();
    }

    /** System Utilities */
    public void modeChanged(int gli_mode) {
	// do nothing at this stage - should we be renewing download options??
    }

    private void addHeader(String name, Color color, JPanel target_pane) {
	JPanel header = new JPanel();
	header.setBackground(color);
	JPanel inner_pane = new JPanel();
	inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(5,5,5,5), BorderFactory.createRaisedBevelBorder()));
	inner_pane.setBackground(color);
	JLabel header_label = new JLabel("<html><strong>" + name + "</strong></html>");
	header_label.setBackground(Configuration.getColor("coloring.collection_heading_background", false));
	header_label.setHorizontalAlignment(JLabel.CENTER);
	header_label.setOpaque(true);

	// Layout
	inner_pane.setLayout(new BorderLayout());
	inner_pane.add(header_label, BorderLayout.CENTER);

	header.setLayout(new BorderLayout());
	header.add(inner_pane, BorderLayout.CENTER);
	target_pane.add(header);
    }

    /** Supporting Functions */
    private Download loadDownload(String download_name, String lang) {
	Document document = null;
	InputStream input_stream = null;
	
	try {
	    if (Gatherer.isGsdlRemote) {
		StringBuffer launch_str = new StringBuffer();
		launch_str.append(Gatherer.cgiBase);
		launch_str.append("launch");
		launch_str.append("?cmd=downloadinfo.pl&xml=&language=");
		launch_str.append(lang);
		launch_str.append("&plug=");
		launch_str.append(download_name);

		String launch = launch_str.toString();		
		System.err.println("*** launch = " + launch);
		
		URL launch_url = new URL(launch);
		URLConnection launch_connection = launch_url.openConnection();
		input_stream = launch_connection.getInputStream();
	    }
	    else {
		ArrayList args_list = new ArrayList();
		String args[] = null;
		if(Utility.isWindows()) {
		    if(Configuration.perl_path != null) {
			args_list.add(Configuration.perl_path);
		    }
		    else {
			args_list.add("Perl.exe");
		    }
		}
		args_list.add(LocalGreenstone.getBinScriptDirectoryPath()+"downloadinfo.pl");
		args_list.add("-xml");
		args_list.add("-language");
		args_list.add(lang);
		args_list.add(download_name);
		
		// Create the process.
		args = (String []) args_list.toArray(new String[0]);
		Runtime runtime = Runtime.getRuntime();
		DebugStream.println("Getting Download Info: "+args_list);
		Process process = runtime.exec(args);
		
		input_stream = process.getErrorStream();
	    }

            document = XMLTools.parseXML(input_stream);
	}
	catch (Exception error) {
	    System.err.println("Failed when trying to parse: " + download_name);
	    error.printStackTrace();
	}

	if(document != null) {
	    return parseXML(document.getDocumentElement());
	}

	return null;
    }

    private Download parseXML(Node root) {
	Download download = new Download();
	String node_name = null;
	for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
	    node_name = node.getNodeName();
	    if(node_name.equalsIgnoreCase("Name")) {
		String name = XMLTools.getValue(node);
		download.setName(name);
	    }
	    else if (node_name.equalsIgnoreCase("Desc")) {
		download.setDescription(XMLTools.getValue(node));
	    }
	    else if (node_name.equalsIgnoreCase("Abstract")) {
		download.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
	    }
	    else if(node_name.equalsIgnoreCase("Arguments")) {
		for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
		    node_name = arg.getNodeName();
                     if(node_name.equalsIgnoreCase("Option")) {
			Argument argument = new Argument((Element)arg);
			argument.parseXML((Element)arg);
			argument.setValue(argument.getDefaultValue());
			download.addArgument(argument);
		     }
		   
		}
	    }
            else if(node_name.equalsIgnoreCase("DownloadInfo")) {
                Download super_download = parseXML(node);
		download.setSuper(super_download);
	    }
	}

	if(download.getName() != null) {
	    return download;
	}
	return null;
    }

    /** Update the previous setup */
    private boolean updateArguments(boolean checkRequired)
    {
	boolean cont = true;
	for(int i = 0; i < options_pane.getComponentCount(); i++) {
	   
	    Component component = options_pane.getComponent(i);
	    if(component instanceof ArgumentControl) {
		cont = cont && ((ArgumentControl)component).updateArgument(checkRequired);
	    }
	}

	if(cont){return true; }

	return false;
    }
    
    /** Generate Controls for Options */
    /* at some stage we should think about which options should be shown for
     * different modes. Currently, always show all options (unless hidden)*/
    private void generateOptions(JPanel options_pane, ArgumentContainer data) {
	options_pane.removeAll();
	/** Create the current option panel */

	ArrayList arguments = data.getArguments(true, false);
      	int mode = Configuration.getMode();
        ArrayList added_arguments = new ArrayList();
     
	for(int i = 0; i < arguments.size(); i++) {
	    Argument argument = (Argument) arguments.get(i);

	    if (argument.isHiddenGLI()) continue;
	    ArgumentControl argument_control = new ArgumentControl(argument,false,null);
	    added_arguments.add(argument_control);
	}
	

       	options_pane.setLayout(new GridLayout(added_arguments.size(),1));
        for(int i = 0; i <  added_arguments.size(); i++) { 
	    options_pane.add((ArgumentControl)added_arguments.get(i));
	    
	}
    }

    /** Behaviour Functions */
    public void afterDisplay() {
	ready = true;
    }


    public void gainFocus() {
	if(!ready) {
	    return;
	}

	// It is also a good time to determine if the download should be enabled - ie if its allowed to be enabled and a valid URL is present in the field.
	download_button_enabled = true;
	//download_button.setEnabled(download_button_enabled);
    }



    public void refresh(int refresh_reason, boolean ready)
    {
    }

    /** Private classes */
    /** This tree provides a 'table of contents' for the various components of the design process (collection configuration in more technical terms). */
    private class DesignTree extends JTree {

	private DesignNode root = null;
	/** Constructor. Automatically generates all of the nodes, in the order of CONTENTS. */
	public DesignTree() {
	    super();
	    resetModel(Configuration.getMode());
	    expandRow(0);
	    setRootVisible(false);
	    setSelectionRow(0);
	}

	/** Reset the model used by the design page contents tree. This is necessary to hide the partitions entry when in lower detail modes
	 * @param mode the current detail mode as an int
	 */
	public void resetModel(int mode) {
	    root = new DesignNode("DOWNLOAD.MODE.Root");
	    // Now add the design categories.
	    for(int i = 0; i < CONTENTS.length; i++) {
		root.add(new DesignNode(CONTENTS[i]));
	    }
	    this.setModel(new DefaultTreeModel(root));
	    updateUI();
	}
	/** Set the current view to the one specified.
	 * @param type the name of the desired view as a String
	 */
	public void setSelectedView(String type) {
	    type = Dictionary.get(type);
	    for(int i = 0; i < root.getChildCount(); i++) {
		DesignNode child = (DesignNode) root.getChildAt(i);
		if(child.toString().equals(type)) {
		    TreePath path = new TreePath(child.getPath());
		    setSelectionPath(path);
		}
	    }
	}
    }

    /** A tree node that retains a reference to one of the possible design sub-views relating to the different sub-managers. */
    private class DesignNode extends DefaultMutableTreeNode {
	/** Constructor.
	 * @param object The <strong>Object</strong> assigned to this node.
	 */
	public DesignNode(String object) {
	    super(object);
	}
	/** Retrieve a textual representation of the object.
	 * @return a String
	 */
	public String toString() {
	    // return Dictionary.get("CDM.GUI." + (String)getUserObject());
	    return Dictionary.get((String) getUserObject());
	}
    }

    /** Listens for selection changes in the 'contents' tree, and switches to the appropriate view. */
    private class TreeListener
	implements TreeSelectionListener {
	/** Called whenever the selection changes, we must update the view so it matches the node selected.
	 * @param event A <strong>TreeSelectionEvent</strong> containing more information about the tree selection.
	 * @see org.greenstone.gatherer.cdm.ClassifierManager
	 * @see org.greenstone.gatherer.cdm.CollectionDesignManager
	 * @see org.greenstone.gatherer.cdm.CollectionMetaManager
	 * @see org.greenstone.gatherer.cdm.FormatManager
	 * @see org.greenstone.gatherer.cdm.LanguageManager
	 * @see org.greenstone.gatherer.cdm.MetadataSetView
	 * @see org.greenstone.gatherer.cdm.SubcollectionManager
	 * @see org.greenstone.gatherer.cdm.TranslationView
	 * @see org.greenstone.gatherer.cdm.PlugInManager
	 */
	public void valueChanged(TreeSelectionEvent event) {
	    if(!tree.isSelectionEmpty()) {
		TreePath path = tree.getSelectionPath();

		DesignNode node = (DesignNode)path.getLastPathComponent();
		String type = (String)node.getUserObject();
		Gatherer.g_man.wait(true);
		if(type == CONTENTS[0]) {
		    mode = "Web";
		    generateOptions(options_pane,(Download)download_map.get(mode));
		}
		else if(type == CONTENTS[1]) {
		    mode = "OAI";
		    generateOptions(options_pane,(Download)download_map.get(mode));
		}
		else if(type == CONTENTS[2]) {
		    mode = "Z3950";
		    generateOptions(options_pane,(Download)download_map.get(mode));
		}
		else if(type == CONTENTS[3]) {
		    mode = "SRW";
		    generateOptions(options_pane,(Download)download_map.get(mode));
		}
		tree.setSelectionPath(path);
		previous_path = path;
		repaint();

		Gatherer.g_man.wait(false);
	    }
	}
    }

    private class ClearCacheListener
	implements ActionListener {
	public void actionPerformed(ActionEvent event) {
	    // Retrieve the cache folder and delete it.
	    Utility.delete(Utility.getCacheDir());
	    // ...and refresh the node in the workspace tree to show it's all gone
	    Gatherer.g_man.refreshWorkspaceTree(WorkspaceTree.DOWNLOADED_FILES_CHANGED);
	}
    }

    private class DownloadButtonListener
	implements ActionListener {
	public void actionPerformed(ActionEvent event) {
	    
	    if(checkURL(true) && checkProxy() == true) {
		
		getter.newDownloadJob((Download)download_map.get(mode) ,mode,proxy_url);
	    }
	}
    }
    

    private boolean checkURL(boolean checkRequired){
	
	if (!updateArguments(checkRequired)){
	    return false;
	}

	Download current_download = (Download)download_map.get(mode);
	Argument arg_url = current_download.getArgument("url");
	
        if (arg_url == null) return true;
 
	String url_str = arg_url.getValue();
	URL url = null;
	try {
	    url = new URL(url_str);
	}
	catch(MalformedURLException error) {
	    JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("Mirroring.Invalid_URL"), Dictionary.get("Mirroring.Invalid_URL_Title"), JOptionPane.ERROR_MESSAGE);
	    return false;
	}

	return true;
    }

    
    private boolean checkProxy(){
	 
	proxy_url = null;

	Download current_download = (Download)download_map.get(mode);
	
        Argument arg = current_download.getArgument("proxy_on");
  
	if (arg == null) return true;
	
	// Determine if we have to use a proxy.
	if(Configuration.get("general.use_proxy", true)) {
          
	    String proxy_host = Configuration.getString("general.proxy_host", true);
	    String proxy_port = Configuration.getString("general.proxy_port", true);
	    // Find out whether the user has already authenticated themselves
	    String user_pass = "";
	    String address = proxy_host + ":" + proxy_port;
	    
            int count = 0;
	    while(count < 3 && (user_pass = (String) GAuthenticator.authentications.get(address)) == null) {
		Authenticator.requestPasswordAuthentication(proxy_host, null, Integer.parseInt(proxy_port), "http://", Dictionary.get("WGet.Prompt"), "HTTP");
		count++;
	    }

	    if(count >= 3) {
		return false;
	    }

	    if(user_pass.indexOf("@") != -1) {
			
		// Write the use proxy command - we don't do this anymore, instead we set environment variables - hopefully these can't be spied on like the follwoing can (using ps) - actually the environment stuff didn't work for windows, so lets go back to this
		if (Utility.isWindows()) {
		     
		   arg.setValue("true");
		   arg.setAssigned(true);

		   arg = current_download.getArgument("proxy_host");
		   arg.setValue(proxy_host);
		   arg.setAssigned(true);

		   arg = current_download.getArgument("proxy_port");
		   arg.setValue(proxy_port);
		   arg.setAssigned(true);

		   arg = current_download.getArgument("user_name");
		   arg.setValue(user_pass.substring(0, user_pass.indexOf("@")));
		   arg.setAssigned(true);

		   arg = current_download.getArgument("user_password");
		   arg.setValue(user_pass.substring(user_pass.indexOf("@") + 1));
		   arg.setAssigned(true);
		  }
	       else{
 		    String user_name = user_pass.substring(0, user_pass.indexOf("@"));
 		    String user_pwd = user_pass.substring(user_pass.indexOf("@") + 1);
 		    proxy_url = user_name+":"+user_pwd+"@"+proxy_host+":"+proxy_port+"/";
		   
 	       }

	       return true;
	    }
	    else{
		return false;
	    }

	}

	return true;
    }

    /*
    private class PreferencesButtonActionListener
    implements ActionListener {
    public void actionPerformed(ActionEvent event) {
    new Preferences(Preferences.CONNECTION_PREFS);
    }
    }*/
    
    private class InformationButtonActionListener
	implements ActionListener {
	public void actionPerformed(ActionEvent event) {
	    //turn off the check for find argument
	    Download current_download = (Download)download_map.get(mode);

            if (!checkProxy() || !checkURL(false) )return;


	    if(server_info != null) {
		server_info.dispose();
	    }

	   
	    Argument arg_url = current_download.getArgument("url");
	    String str_url = "";

	    if( arg_url!= null && arg_url.isAssigned()) {
		str_url = arg_url.getValue();
	    }

            
 	    server_info = new ServerInfoDialog(str_url ,proxy_url, mode,(Download)download_map.get(mode));
 	    
	}
    }

     private class PreferencesButtonActionListener
	implements ActionListener {
	public void actionPerformed(ActionEvent event) {
	    new Preferences(Preferences.CONNECTION_PREFS);
	}
    }
}
