/**
 *#########################################################################
 *
 * 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.
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * 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.
 *
 * 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.
 *
 * 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.file;

import java.io.*;
import java.util.regex.*;
import javax.swing.table.*;
import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.gui.FileAssociationDialog;
import org.greenstone.gatherer.gui.PreviewCommandDialog;
import org.greenstone.gatherer.util.ExternalProgram;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.Utility;
import org.greenstone.gatherer.util.XMLTools;
import org.w3c.dom.*;

public class FileAssociationManager 
    extends AbstractTableModel {
    static final public String FILENAME_ARG = "%1";
    static final private String ESCAPE = "\\\\"; // '\'
    static final private String ESCAPED_ESCAPE = "\\\\\\\\"; // '\\'
    static final private String SPACE = " ";
    static final private String ESCAPED_SPACE = "\\\\ ";
    private Element associations_element;
    private File data_file;   

    public FileAssociationManager() {
	// Retrieve the associations_element from the config
	associations_element = Configuration.getFileAssociations();
	// Initialize the associations. This involves looking through all current associations searching for those with a command of "".
	if(associations_element != null) {
	    NodeList entries = associations_element.getElementsByTagName(StaticStrings.ENTRY_ELEMENT);
	    for(int i = 0; i < entries.getLength(); i++) {
		Element entry = (Element) entries.item(i);
		String command = XMLTools.getValue(entry);
		// If we encounter a command of ""...
		if(command.length() == 0) {
		    // if we are on windows, we default to the start command
		    if(Utility.isWindows()) {
			if (Utility.isWindows9x()) {
			    XMLTools.setValue(entry, StaticStrings.WIN_9X_OPEN_COMMAND);
			} else {
			    XMLTools.setValue(entry, StaticStrings.WIN_OPEN_COMMAND);
			}
		    }
		    // and if we are on mac, we default to the open program
		    else if(Utility.isMac()) {
			XMLTools.setValue(entry, StaticStrings.MAC_OPEN_COMMAND);
		    }
		}
		command = null;
		entry = null;
	    }
	    entries = null;
	}
	else {
	    DebugStream.println("Didn't parse anything. About to crash.");
	}
    }
    
    public void edit() {
	FileAssociationDialog dialog = new FileAssociationDialog(this);
	dialog.display(null);
	dialog = null;		  
    }

    public String getBrowserCommand(String url) {
	DebugStream.println("Get browser command: " + url);
	// First off we try to retrieve one from the configuration
	String command = Configuration.getPreviewCommand();
	// If that worked, substitute in the url and return
	if(command != null && command.length() > 0) {
	    command = command.replaceAll("%1", url);
	    DebugStream.println("Result = " + command);
	    return command;
	}
	command = null;
	// Failing that we have a guess at a sensible default 
	if(Utility.isWindows()) {
	    // we use cmd and start
	    if (Utility.isWindows9x()) {
		command = StaticStrings.WIN_9X_OPEN_COMMAND;//"command.com /c start \""+url+"\"";
	    } else {
		command = StaticStrings.WIN_OPEN_COMMAND;//"cmd.exe /c start \"\" \""+url+"\"";
	    }
	} else if (Utility.isMac()) {
	    command = StaticStrings.MAC_OPEN_COMMAND; // "open %1"
	} else {
	    // we try to look for a browser
	    String [] browsers = new String [] {"mozilla", "netscape"};
	    for (int i=0; i<browsers.length; i++) {
		if (isAvailable(browsers[i])) {
		    command = browsers[i]+ " %1";
		    break;
		}
	    }
	}

	// if we still haven't found something, prompt the user
	if (command == null) {
	    PreviewCommandDialog dialog = new PreviewCommandDialog();
	    command = dialog.display();
	    dialog.dispose();
	    dialog = null;
	}

	// Store the result if any
	if(command != null && !command.equals("")) {
	    Configuration.setPreviewCommand(command);
	    command = command.replaceAll(FILENAME_ARG, url);
	    DebugStream.println("Result = " + command);
	    return command;
	}
	// if we haven't got a command by now, we'll never get one
	DebugStream.println("Result = null");
	return null;
	
    }

    public int getColumnCount() {
	return 2;
    }

    public String getColumnName(int column) {
	String name;
	switch(column) {
	case 0:
	    name = Dictionary.get("FileAssociationDialog.Table.Extension");
	    break;
	default:
	    name = Dictionary.get("FileAssociationDialog.Table.Command");
	}
	return name;
    }
   
    public String [] getCommand(File file) {
	String command = null;
	String [] commands = null;
	if(file.isFile()) {
	    // Determine extension
	    String filename = file.getAbsolutePath();
	    String extension = filename.substring(filename.lastIndexOf(".") + 1);
	    // Try to retrieve a value from cache
	    Element entry = getCommand(extension);
	    if(entry != null) {
		///ystem.err.println("Retrieved Value From Cache");
		command = XMLTools.getValue(entry); 
	    }
	    if(command == null || command.length() == 0) {
		///ystem.err.println("No Existing Command");
		// If command is null, and we are on windows try searching the registry.
		if(Utility.isWindows()) {
		    //try the start command
		    if (Utility.isWindows9x()) {
			command = StaticStrings.WIN_9X_OPEN_COMMAND;
		    } else {
			command = StaticStrings.WIN_OPEN_COMMAND;
		    }
		    
		}
		
		// If we are on a mac, default to using the open program
		else if(Utility.isMac()) {
		    command = StaticStrings.MAC_OPEN_COMMAND;
		}

		// Otherwise display the dialog and ask the user to enter launching command.
		if(command == null || command.length() == 0) {
		    ///ystem.err.println("Show Dialog");
		    // Show the dialog which forces a user to select the launch command for a certain file.
		    FileAssociationDialog dialog = new FileAssociationDialog(this);
		    command = dialog.display(extension);
		    dialog = null;
		}

		// Hopefully by now we have a command, or else we're never going to get one. Add the association.
		if (command != null && !command.equals("")) {
		    // If no previous entry existed create one.
		    if(entry == null) {
			entry = associations_element.getOwnerDocument().createElement(StaticStrings.ENTRY_ELEMENT);
			entry.setAttribute(StaticStrings.EXTENSION_ATTRIBUTE, extension);
			associations_element.appendChild(entry);
		    }
		    // Replace the text in this node. Remember to replace the dummy filename with %1 - I dont think the filename will ever be in the comand now
		    //XMLTools.setValue(entry, command.replaceAll(filename, FILENAME_ARG));
		    XMLTools.setValue(entry, command);
		}
	    }

	    if (command != null && !command.equals("")) {
		// Make the command into a string []		
		commands = command.split(" ");

		// Now substitute any occurrences of %1 with its filename
		// Note this is done after the split on spaces to avoid
		// any conflict with filenames with spaces in them.

		// We have to fix filename under windows to escape the backslashes
		filename = filename.replaceAll(ESCAPE, ESCAPED_ESCAPE);

		for (int i=0; i<commands.length; i++) {
		    // Replace %1 with the appropriate filename
		    commands[i] = commands[i].replaceAll(FILENAME_ARG, filename);
		}
	    }
	    
	    entry = null;
	    extension = null;
	    filename = null;
	}
	return commands;
    }

    public Element getCommand(String target_extension) {
	NodeList entries = associations_element.getElementsByTagName(StaticStrings.ENTRY_ELEMENT);
	for(int i = 0; i < entries.getLength(); i++) { 
	    Element entry = (Element) entries.item(i);
	    String extension = entry.getAttribute(StaticStrings.EXTENSION_ATTRIBUTE);
	    if(extension.equalsIgnoreCase(target_extension)) {
		entries = null;
		extension = null;
		return entry;
	    }
	}
	entries = null;
	return null;
    }

    public String getCommandString(String target_extension) {
	Element entry = getCommand(target_extension);
	if(entry != null) {
	    return XMLTools.getValue(entry);
	}
	else {
	    return "";
	}
    }

    public String getExtension(int index) {
	NodeList entries = associations_element.getElementsByTagName(StaticStrings.ENTRY_ELEMENT);
	if(0 <= index && index < entries.getLength()) {
	    Element entry = (Element) entries.item(index);
	    return entry.getAttribute(StaticStrings.EXTENSION_ATTRIBUTE);
	}
	return "";
    }

    public int getRowCount() {
	return size();
    }

    public Object getValueAt(int row, int column) {
	String extension = getExtension(row);
	switch(column) {
	case 0:
	    return extension;
	default:
	    return getCommandString(extension);
	}
    }

    public void save() {
    }

    public void setCommand(String extension, String command) {
	DebugStream.println("Set Launch: " + extension + " with " + command);
	// Retrieve any existing entry for this extension
	Element entry = getCommand(extension);
	// If no previous entry existed create one.
	if(entry == null && command != null) {
	    entry = associations_element.getOwnerDocument().createElement(StaticStrings.ENTRY_ELEMENT);
	    entry.setAttribute(StaticStrings.EXTENSION_ATTRIBUTE, extension);
	    associations_element.appendChild(entry);
	}

	if(command != null) {
	    // Replace the text in this node. If the user has used filename instead of %1 then too bad.
	    XMLTools.setValue(entry, command);
	}
	else {
	    // Remove the entry
	    associations_element.removeChild(entry);
	}
	entry = null;
	fireTableDataChanged(); // Can't be anymore efficient as DOM does not gareuntee ordering of new child nodes is consistant
    }

    public int size() {
	NodeList entries = associations_element.getElementsByTagName(StaticStrings.ENTRY_ELEMENT);
	return entries.getLength();
    }


    protected boolean isAvailable(String program) {
	try {
	    ExternalProgram e = new ExternalProgram("which", program);
	    e.exitProgram();
	    String out = e.getLineOfProgramOutput();
	    if (out == null) {
		return false;
	    }
	    return true;
	} catch (Exception exc) {
	    return false;
	}
    }
}
