/**
 *#########################################################################
 *
 * 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.util.ArrayList;
import javax.swing.*;
import javax.swing.event.*;
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.collection.BasicCollectionConfiguration;
import org.greenstone.gatherer.collection.CollectionManager;
import org.greenstone.gatherer.shell.GShell;
import org.greenstone.gatherer.shell.GShellEvent;
import org.greenstone.gatherer.shell.GShellListener;
import org.greenstone.gatherer.util.ArrayTools;
import org.greenstone.gatherer.util.CheckList;
import org.greenstone.gatherer.util.CheckListEntry;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.Utility;

/** This class provides the functionality to write a CD-ROM/DVD image of current collections from the GSDLHOME/collect/ directory to CDROM. The user chooses the collection from a list, where each entry also displays details about itself, confirms the delete of a collection by checking a checkbox then presses the ok button to actually delete the collection.
    Copied from DeleteCollectionPrompt
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
public class WriteCDImagePrompt
    extends ModalDialog 
    implements GShellListener {
    
    private OKButtonListener ok_button_listener;
    private ArrayList all_collections = null;
    private ArrayList selected_collections = null;
    /** The list of collections to include in exported cd-rom/dvd image */
    private CheckList list = null;
    /** The currently selected collection for deletion. */
    private BasicCollectionConfiguration collection = null;
    /** A reference to ourself so any inner-classes can dispose of us. */
    private WriteCDImagePrompt prompt = null;
    /** The close button, which exits the prompt without deleting anything. */
    private JButton cancel_button = null;
    /** The ok button which causes the selected collection to be deleted. */
    private JButton ok_button = null;
    /** The label above details. */
    private JLabel details_label = null;
    /** The label above the list. */
    private JLabel list_label = null;
    /** The text area used to display details about the collection selected. */
    private JTextArea details_textarea = null;
    /** The text area used to display instructions for the cd-rom/dvd export */
    private JTextArea instructions_textarea;

    /** The radio button for switching on the noinstall option */
    private JRadioButton noinstall_button = null;
    private JRadioButton install_button = null;
    private JTextField title_field = null;
    private JTextField estimated_size_field = null;
    private JLabel title_label = null;
    private JLabel estimated_size_label = null;
    /** A string array used to pass arguments to the exportcol.pl script */
    private String args[] = null;
    private String cd_title = null;
    /** whether the exporting was successful or not */
    private boolean successful = false;
    /** whether we are trying to export or not */
    private boolean exporting = false;
    /** the error message if any */
    private StringBuffer error_message = null;
    /** This is the size of the exported stuff on its own without collections. */
    private long total_exported_size = 14000000;
    /** The size of the export prompt screen. */
    public static final Dimension SIZE = new Dimension(500, 500);

    /** Constructor.
     * @see org.greenstone.gatherer.collection.WriteCDImagePrompt.CancelButtonListener
     * @see org.greenstone.gatherer.collection.WriteCDImagePrompt.CollectionListListener
     * @see org.greenstone.gatherer.collection.WriteCDImagePrompt.OKButtonListener
     */
    public WriteCDImagePrompt() {
	super(Gatherer.g_man, true);
	cancel_button = new GLIButton(Dictionary.get("General.Close"), Dictionary.get("General.Close_Tooltip"));
	
	details_textarea = new JTextArea(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
	details_textarea.setEditable(false);
	
	details_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_Details"));
	
	instructions_textarea = new JTextArea(Dictionary.get("WriteCDImagePrompt.Instructions"));
	instructions_textarea.setCaretPosition(0);
	instructions_textarea.setEditable(false);
	instructions_textarea.setLineWrap(true);
	instructions_textarea.setRows(4);
	instructions_textarea.setWrapStyleWord(true);
	
	all_collections = new ArrayList();
	list = new CheckList(true);
	list_label = new JLabel(Dictionary.get("DeleteCollectionPrompt.Collection_List"));
	
	ok_button = new GLIButton(Dictionary.get("WriteCDImagePrompt.Export"), Dictionary.get("WriteCDImagePrompt.Export_Tooltip"));
	
	title_field = new JTextField();
	// Dictionary.setTooltip(title_field, "WriteCDImagePrompt.CD_Name_Tooltip");
	title_label = new JLabel(Dictionary.get("WriteCDImagePrompt.CD_Name"));
	

	estimated_size_field = new JTextField();
	estimated_size_field.setEditable(false);

	// work out the size of the images directory - could be large if they
	// have the classic interface pack installed
	total_exported_size += getFileSize(new File(LocalGreenstone.getDirectoryPath()+File.separator+"images"));
	estimated_size_field.setText(Utility.formatFileLength(total_exported_size));
	estimated_size_field.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
	estimated_size_label = new JLabel(Dictionary.get("WriteCDImagePrompt.Size_Label"));
	
	scanForCollections();
	list.setListData(all_collections);

	prompt = this;
	setSize(SIZE);
	setTitle(Dictionary.get("WriteCDImagePrompt.Export"));
	
	setJMenuBar(new SimpleMenuBar("exportingcollections")); 
	cancel_button.addActionListener(new CancelButtonListener());
	list.addListSelectionListener(new CollectionListListener());
	list.clearSelection();
	list.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	ok_button_listener = new OKButtonListener();
	ok_button.addActionListener(ok_button_listener);
	ok_button.setEnabled(false);
	//title.getDocument().addDocumentListener(new DocumentListener());
	
	noinstall_button = new JRadioButton(Dictionary.get("WriteCDImagePrompt.NoInstall"));
	noinstall_button.setToolTipText(Dictionary.get("WriteCDImagePrompt.NoInstall_Tooltip"));
	
	
	install_button = new JRadioButton(Dictionary.get("WriteCDImagePrompt.Install"));
	install_button.setToolTipText(Dictionary.get("WriteCDImagePrompt.Install_Tooltip"));
	

	
    }

    /** Destructor. */
    public void destroy() {
	all_collections.clear();
	all_collections = null;
	cancel_button = null;
	details_textarea = null;
	details_label = null;
	list = null;
	ok_button = null;
	prompt = null;
	if (selected_collections!=null) {
	    selected_collections.clear();
	    selected_collections = null;
	}
	title_field = null;
	title_label = null;
    }

    /** This method causes the modal prompt to be displayed. 
     * returns true if it has exported the collections that are currently selected */
    public boolean display() {

	// Radio buttons
	ButtonGroup radio_group = new ButtonGroup();
	radio_group.add(install_button);
	install_button.setSelected(true);
	radio_group.add(noinstall_button);

	// Top pane
	JPanel instructions_pane = new JPanel(new BorderLayout());
	instructions_pane.setBorder(BorderFactory.createEmptyBorder(5,5,0,5));
	instructions_pane.add(new JScrollPane(instructions_textarea), BorderLayout.CENTER);
	
	title_label.setBorder(BorderFactory.createEmptyBorder(0,5,0,15));

	JPanel title_pane = new JPanel(new BorderLayout());
	title_pane.add(title_label, BorderLayout.WEST);
	title_pane.add(title_field, BorderLayout.CENTER);
	title_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));

	JPanel radio_pane = new JPanel(new GridLayout(2,1));
	install_button.setBackground(Configuration.getColor("coloring.collection_heading_background", true));
	noinstall_button.setBackground(Configuration.getColor("coloring.collection_heading_background", true));
	radio_pane.add(install_button);
	radio_pane.add(noinstall_button);
	
	
	JPanel options_pane = new JPanel(new BorderLayout());
	options_pane.add(title_pane, BorderLayout.NORTH);
	options_pane.add(radio_pane, BorderLayout.CENTER);
	instructions_pane.add(options_pane, BorderLayout.SOUTH);

	// Central pane
	JPanel list_pane = new JPanel(new BorderLayout());
	list_pane.add(list_label, BorderLayout.NORTH);
	list_pane.add(new JScrollPane(list), BorderLayout.CENTER);
	list_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));

	JPanel details_pane = new JPanel(new BorderLayout());
	details_pane.add(details_label, BorderLayout.NORTH);
	details_pane.add(new JScrollPane(details_textarea), BorderLayout.CENTER);
	details_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

	JPanel central_pane = new JPanel(new GridLayout(2, 1));
	central_pane.add(list_pane);
	central_pane.add(details_pane);
	central_pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

	// Lower pane
	JPanel estimated_size_pane = new JPanel(new BorderLayout());
	estimated_size_pane.add(estimated_size_label, BorderLayout.WEST);
	estimated_size_pane.add(estimated_size_field, BorderLayout.CENTER);
	estimated_size_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));

	JPanel button_pane = new JPanel(new GridLayout(1, 2));
	button_pane.add(ok_button);
	button_pane.add(cancel_button);
	button_pane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

	JPanel lower_pane = new JPanel(new BorderLayout());
	lower_pane.add(estimated_size_pane, BorderLayout.NORTH);
	lower_pane.add(button_pane, BorderLayout.SOUTH);
	lower_pane.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));

	// Final.
	JPanel content_pane = (JPanel)this.getContentPane();
	content_pane.setLayout(new BorderLayout());
	content_pane.add(instructions_pane, BorderLayout.NORTH);
	content_pane.add(central_pane, BorderLayout.CENTER);
	content_pane.add(lower_pane, BorderLayout.SOUTH);

	// Center and display.
	Dimension screen_size = Configuration.screen_size;
	this.setLocation((screen_size.width - SIZE.width) / 2, (screen_size.height - SIZE.height) / 2);
	this.setVisible(true); // blocks until the dialog is killed
	return true;
	
    }

    
    protected long getFileSize(File file)
    {
	long file_size = 0;
	
	// Directory case
	if (file.isDirectory()) {
	    File files[] = file.listFiles();
	    for (int i = 0; i < files.length; i++) {
		file_size += getFileSize(files[i]);
	    }
	}
	// File case
	else {
	    file_size = file.length();
	}
	
	return file_size;
    }

    /** This method calls the builcol.pl scripts via a GShell so as to not lock up the processor.
     * @see org.greenstone.gatherer.Configuration
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.collection.Collection
     * @see org.greenstone.gatherer.gui.BuildOptions
     * @see org.greenstone.gatherer.shell.GShell
     * @see org.greenstone.gatherer.shell.GShellListener
     * @see org.greenstone.gatherer.shell.GShellProgressMonitor
     * @see org.greenstone.gatherer.util.Utility
     */
    public void writeCDImageCollections()
    {
	DebugStream.println("WriteCDImagePrompt.writeCDImageCollections()");

	int num_collections = selected_collections.size();
	if (num_collections == 0) return;
	cd_title = title_field.getText();
	cd_title = cd_title.trim();
	cd_title = cd_title.replaceAll("\"","");
	
	// Generate the exportcol.pl command
	ArrayList command_parts_list = new ArrayList();
	if (Utility.isWindows() && (!Gatherer.isGsdlRemote)) {
	    command_parts_list.add(Configuration.perl_path);
	    command_parts_list.add("-S");
	}
	command_parts_list.add(LocalGreenstone.getBinScriptDirectoryPath() + "exportcol.pl");

	command_parts_list.add("-gli");
	command_parts_list.add("-language");
	command_parts_list.add(Configuration.getLanguage());

	if (cd_title.equals("")) {
	    command_parts_list.add("-cdname");
	    command_parts_list.add("Greenstone Collections");

	    command_parts_list.add("-cddir");
	    command_parts_list.add("exported_collections");
	}
	else {
	    command_parts_list.add("-cdname");
	    command_parts_list.add(cd_title);

	    String cd_dir = "exported_"+cd_title.replaceAll("\\s","");
	    command_parts_list.add("-cddir");
	    command_parts_list.add(cd_dir);
	}

	if (noinstall_button.isSelected()) {
	    command_parts_list.add("-noinstall");
	}

	for (int i = 0; i < num_collections; i++) {
	    command_parts_list.add(((BasicCollectionConfiguration) selected_collections.get(i)).getShortName());
	}

	DebugStream.print("export command = ");
	for (int i = 0; i < command_parts_list.size(); i++) {
	    DebugStream.print(command_parts_list.get(i) + " ");
	}
	DebugStream.println("");
	// Run the exportcol.pl command
	String[] command_parts = (String[]) command_parts_list.toArray(new String[0]);
	GShell process = new GShell(command_parts, GShell.CDIMAGE, 3, this, null, GShell.GSHELL_CDIMAGE);
	process.start();
	//process.run();
	DebugStream.println("WriteCDImagePrompt.writeCDImageCollections().return");
	
    }

    /** Shows an export complete prompt. 
     * @param success A <strong>boolean</strong> indicating if the collection was successfully deleted.
     * @see org.greenstone.gatherer.collection.Collection
     */
    public void resultPrompt(boolean success, String extra) {
	args = new String[2];
	StringBuffer coll_names = new StringBuffer();
	for (int i=0; i<selected_collections.size();i++) {
	    if (i>0) {
		coll_names.append(", ");
	    }
	    BasicCollectionConfiguration complete_collection = (BasicCollectionConfiguration)selected_collections.get(i);
	    coll_names.append(complete_collection.getName() + StaticStrings.SPACE_CHARACTER + StaticStrings.OPEN_PARENTHESIS_CHARACTER + complete_collection.getShortName() + StaticStrings.CLOSE_PARENTHESIS_CHARACTER);
	    complete_collection = null;
	}
	
	args[0] = coll_names.toString();
	args[1] = LocalGreenstone.getTmpDirectoryPath();
	if(cd_title.equals("")) {
	    args[1] += "exported_collections";
	} else {
	    args[1] += "exported_"+cd_title.replaceAll("\\s","");
	}
	String title;
	String label;
	if (success) {
	    title = Dictionary.get("WriteCDImagePrompt.Successful_Title");
	    label = Dictionary.get("WriteCDImagePrompt.Successful_Export", args);
	} else {
	    title = Dictionary.get("WriteCDImagePrompt.Failed_Title");
	    label = Dictionary.get("WriteCDImagePrompt.Failed_Export", args);
	}
	SimpleResultDialog result_dialog = new SimpleResultDialog(title, label, extra);
	result_dialog.setVisible(true); // Blocks
	result_dialog.dispose();
	result_dialog = null;
    }

    /** Method to scan the collect directory retrieving and reloading each collection it finds, while building the list of known collections.
     * @see org.greenstone.gatherer.Configuration
     * @see org.greenstone.gatherer.Gatherer
     * @see org.greenstone.gatherer.util.ArrayTools
     * @see org.greenstone.gatherer.util.Utility
     */
    private void scanForCollections() {
	// Start at the collect dir.
	File collect_directory = new File(Gatherer.getCollectDirectoryPath());
	if (collect_directory.exists()) {
	    // Now for each child directory see if it contains a .col file and
	    // if so try to load it..
	    File collections[] = collect_directory.listFiles();
	    ArrayTools.sort(collections);
	    for(int i = 0; collections != null && i < collections.length; i++) {
		if(collections[i].isDirectory() && !collections[i].getName().equals(StaticStrings.MODEL_COLLECTION_NAME)) {
		    File config_file = new File(collections[i], Utility.CONFIG_FILE);
		    if (config_file.exists()) {
			BasicCollectionConfiguration config = new BasicCollectionConfiguration(config_file);
			all_collections.add(config);		        
			config = null;
		    }
		}
	    }
	}
	// Otherwise the collect directory doesn't actually exist, so there ain't much we can do.
    }


    /** All implementation of GShellListener must include this method so the listener can be informed of messages from the GShell.
     * @param event A <strong>GShellEvent</strong> that contains, amoung other things, the message.
     */
    public synchronized void message(GShellEvent event) {
	// Ignore the messages from RecPlug with 'show_progress' set (used for progress bars)
	String message = event.getMessage();
	if (message.startsWith("exportcol.pl>")) {
	    message = message.substring(13);
	    //DebugStream.println("message = "+event.getMessage());
	    error_message.append(message);
	    error_message.append("\n");
	}
    }

    /** All implementation of GShellListener must include this method so the listener can be informed when a GShell begins its task. Implementation side-effect, not actually used.
     * @param event A <strong>GShellEvent</strong> that contains details of the initial state of the <strong>GShell</strong> before task comencement.
     */
    public synchronized void processBegun(GShellEvent event) {
	// We don't care. 
    }
    /** All implementation of GShellListener must include this method so the listener can be informed when a GShell completes its task.
     * @param event A <strong>GShellEvent</strong> that contains details of the final state of the <strong>GShell</strong> after task completion.
     */
    public synchronized void processComplete(GShellEvent event) {
	successful = false;
	if(event.getStatus() == GShell.OK) {
	    if(event.getType() == GShell.CDIMAGE) {
		successful = true;
	    }
	}
	ok_button_listener.processComplete();
    }

    /** A button listener implementation, which listens for actions on the close button and disposes of the dialog when detected. */
    private class CancelButtonListener 
	implements ActionListener {
	/** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
	 * @param event An <strong>ActionEvent</strong> containing all the relevant information garnered from the event itself.
	 */
	public void actionPerformed(ActionEvent event) {
	    prompt.dispose();
	}
    }

    /** This private class listens for selection events in from the list and then displays the appropriate details for that collection.
     */
    private class CollectionListListener 
	implements ListSelectionListener
    {
	/** Any implementation of ListSelectionListener must include this method so we can be informed when the list selection changes.
	 * @param  event a <strong>ListSelectionEvent</strong> containing all the relevant information garnered from the event itself
	 */
	public void valueChanged(ListSelectionEvent event)
	{
	    // Wait for things to settle down a bit
	    if (event.getValueIsAdjusting()) {
		return;
	    }

	    // Can only export when something is ticked
	    ok_button.setEnabled(!list.isNothingTicked());

	    if (list.isSelectionEmpty()) {
		// This only happens when the dialog is first entered
		details_textarea.setText(Dictionary.get("DeleteCollectionPrompt.No_Collection"));
		return;
	    }

	    collection = (BasicCollectionConfiguration) ((CheckListEntry) list.getSelectedValue()).getObject();
	    args = new String[3];
	    args[0] = collection.getCreator();
	    args[1] = collection.getMaintainer();
	    args[2] = collection.getDescription();
	    details_textarea.setText(Dictionary.get("DeleteCollectionPrompt.Details", args));
	    details_textarea.setCaretPosition(0);

	    // Find the size of the "etc", "images" and "index" directories of the collection
	    String collection_directory_path = CollectionManager.getCollectionDirectoryPath(collection.getShortName());
	    File etc_directory = new File(collection_directory_path + "etc");
	    File images_directory = new File(collection_directory_path + "images");
	    File index_directory = new File(collection_directory_path + "index");
	    long collection_size_built = getFileSize(etc_directory) + getFileSize(images_directory) + getFileSize(index_directory);

	    // Add/subtract it from the total, depending on whether the collection has just been ticked/unticked
	    if (((CheckListEntry) list.getSelectedValue()).isSelected()) {
		total_exported_size += collection_size_built;
	    }
	    else {
		total_exported_size -= collection_size_built;
	    }

	    // Update the size field
	    estimated_size_field.setText(Utility.formatFileLength(total_exported_size));
	}
    }


    /** The OK button listener implementation. */
    private class OKButtonListener 
	implements ActionListener {
	private Component glass_pane;
	private MouseListener mouse_blocker_listener;
	private ProgressDialog progress_dialog;

	/** Any implementation of ActionListener must include this method so we can be informed when the button is actioned.
	 * @param event An <strong>ActionEvent</strong> containing all the relevant information garnered from the event itself.
	 * @see org.greenstone.gatherer.Configuration
	 * @see org.greenstone.gatherer.Gatherer
	 * @see org.greenstone.gatherer.util.Utility
	 */
	public void actionPerformed(ActionEvent event) {
	    ///ystem.err.println("OK Clicked");
	    // Make sure there are some colls specified
	    selected_collections = list.getTicked();
	    error_message = new StringBuffer();
	    
	    // Set the cursor to hourglass
	    glass_pane = getGlassPane();
	    mouse_blocker_listener = new MouseAdapter() {};
	    glass_pane.addMouseListener(mouse_blocker_listener);
	    glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
	    glass_pane.setVisible(true);

	    // Export the selected collection.
	    ///ystem.err.println("Write CD-ROM/DVD image for named collections");
	    writeCDImageCollections();

	    // Show progress dialog
	    ///ystem.err.println("Showing progress dialog");
	    progress_dialog = new ProgressDialog();
	    progress_dialog.setVisible(true);
	}

	public void processComplete() {
	    ///ystem.err.println("Process complete");
	    // Dispose of progress dialog
	    progress_dialog.setVisible(false);
	    progress_dialog.dispose();
	    progress_dialog = null;

	    // unset the cursor
	    glass_pane.setVisible(false);
	    glass_pane.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
	    glass_pane.removeMouseListener(mouse_blocker_listener);
	    glass_pane = null;
	    mouse_blocker_listener= null;
	    
	    if (successful) {
		resultPrompt(true, error_message.toString());
	    } else {
		resultPrompt(false, error_message.toString());
	    }
	    error_message = null;
	}

	private class ProgressDialog 
	    extends ModalDialog {
	    
	    private Dimension size = new Dimension(400,65);
	    
	    public ProgressDialog() {
		super(Gatherer.g_man, Dictionary.get("WriteCDImagePrompt.Title"), true);
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		setSize(size);
		JPanel content_pane = (JPanel) getContentPane();
		JLabel progress_label = new JLabel(Dictionary.get("WriteCDImagePrompt.Progress_Label"));
		
		JProgressBar progress_bar = new JProgressBar();
		progress_bar.setIndeterminate(true);
		content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
		content_pane.setLayout(new BorderLayout());
		content_pane.add(progress_label, BorderLayout.NORTH);
		content_pane.add(progress_bar, BorderLayout.CENTER);
		// Position
		Rectangle frame_bounds = Gatherer.g_man.getBounds();
		setLocation(frame_bounds.x + (frame_bounds.width - size.width) / 2, frame_bounds.y + (frame_bounds.height - size.height) / 2);
	    }
	}
    }
}
