/**
 *#########################################################################
 *
 * 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.*;
import javax.swing.*;
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.collection.CollectionTree;
import org.greenstone.gatherer.collection.CollectionTreeNode;
import org.greenstone.gatherer.collection.FullCollectionTree;
import org.greenstone.gatherer.collection.FullCollectionTreeNode;
import org.greenstone.gatherer.file.FileNode;
import org.greenstone.gatherer.file.FileOpenActionListener;
import org.greenstone.gatherer.file.FileQueue;
import org.greenstone.gatherer.file.FileSystemModel;
import org.greenstone.gatherer.file.RecycleBin;
import org.greenstone.gatherer.file.WorkspaceTree;
import org.greenstone.gatherer.file.WorkspaceTreeNode;
import org.greenstone.gatherer.gui.tree.DragTree;
import org.greenstone.gatherer.util.DragComponent;
import org.greenstone.gatherer.util.DragGroup;
import org.greenstone.gatherer.util.JarTools;
import org.greenstone.gatherer.util.Utility;

import org.greenstone.gatherer.gui.TestingPreparation;

/** The collection pane is analogous with a file manager. It is there that the user chooses which files to include in their collection and what structure the file hierarchy should take. The later aspect is not important for the Greenstone Suite, but is usefull for grouping files for ease of metadata markup. The view essentially consists of two file trees, one denoting the entire source workspace and the other the files within your collection. The trees themselves have a title bar at the top, a filter control at the bottom, and are coloured to indicate activity (grey for disabled). The remainder of the screen is taken by a status area, to indicate current file job progress during copying etc, and three buttons for controlling features of the view.
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
public class GatherPane
    extends JPanel
    implements ActionListener
{

    private CardLayout card_layout = null;
    private JPanel card_pane = null;
    /** The name of the panel containing the "collection loaded" (collection tree) card. */
    private String COLLECTION_LOADED_CARD = "";
    /** The name of the panel containing the "no collection loaded" placeholder */
    private String NO_COLLECTION_LOADED_CARD = "No collection loaded";
    /** The group encompassing all of the components available as drop targets for drag and drop actions. Required so that only one component renders the ghost and higlights itself as a target, which the other members are restored to their original, pristine, condition. */
    private DragGroup group = null;
    /** The tree showing the files within the collection. */
    private DragTree collection_tree = null;
    /** The threaded queue that handles the actually movement of files, so that the gui remains responsive. */
    private FileQueue file_queue = null;
    /** The filter currently applied to the collection tree. */
    private Filter collection_filter = null;
    /** The filter currently applied to the workspace tree. */
    private Filter workspace_filter = null;
    /** The button used to cancel all pending file queue jobs. */
    private JButton stop_action =  null;
    /** The button used to create a new folder in the collection tree. */
    private JButton new_folder =  null;
    /** The button used to add a new file in the collection tree */
    private JButton new_file = null;
    /** The label shown at the top of the collection tree. */
  private JPanel button_pane = null;
    private JLabel collection_label = null;
    /** The label shown in the status area explaining the file apon which action is taking place. */
    private JLabel filename_label = null;
    /** The label shown explaining the current state of the file queue thread. */
    private JLabel status_label	= null;
    /** The label at the top of the workspace tree. */
    private JLabel workspace_label = null;
    /** The panel that contains the collection tree. */
    private JPanel collection_pane = null;
    /** The panel that contains the various controls including the status area. */
    private JPanel control_pane = null;
    /** The panel that contains the workspace tree. */
    private JPanel workspace_pane = null;
    /** The scrollable area into which the collection tree is placed. */
    private JScrollPane collection_scroll = null;
    /** The scrollable area into which the workspace tree is placed. */
    private JScrollPane workspace_scroll = null;
    /** A split pane seperating the two trees, allowing for the screen real-estate for each to be changed. */
    private JSplitPane tree_pane = null;
    /** The minimum size a gui component can become. */
    static private Dimension MIN_SIZE = new Dimension( 90,  90);
    /** The default size of the status area. */
    static private Dimension STATUS_SIZE = new Dimension(450, 120);
    /** The initial size of the trees. */
    static private Dimension TREE_SIZE = new Dimension(400, 430);

    /** The tree showing the available source workspace. */
    public WorkspaceTree workspace_tree = null;

  private int pane_mode = Utility.IMPORT_MODE; // default to IMPORT_MODE
    public GatherPane(int pane_mode)
    {
      this.pane_mode = pane_mode;
	this.group = new DragGroup();
	this.file_queue = Gatherer.f_man.getQueue();
        this.setComponentOrientation(org.greenstone.gatherer.Dictionary.getOrientation());
	// Create components.
	stop_action = new GLIButton(Dictionary.get("Collection.Stop"), Dictionary.get("Collection.Stop_Tooltip"));
	stop_action.addActionListener(this);
	stop_action.setEnabled(false);
	file_queue.registerStopButton(stop_action);
		
	new_folder = new GLIButton(JarTools.getImage("new-folder.png"), Dictionary.get("Collection.New_Folder_Tooltip"));
	new_folder.addActionListener(this);
	new_folder.setEnabled(false);
	new_folder.setMinimumSize(MIN_SIZE);
	new_folder.setPreferredSize(MIN_SIZE);

	new_file = new GLIButton(JarTools.getImage("file-upload.png"), Dictionary.get("Collection.New_File_Tooltip"));
	new_file.addActionListener(this);
	new_file.setEnabled(false);
	new_file.setMinimumSize(MIN_SIZE);
	new_file.setPreferredSize(MIN_SIZE);

    }

    /** Any implementation of ActionListener requires this method so that when an action is performed the appropriate effect can occur. In this case there are three valid possibilities. If the action occured on the recycle bin, then delete the current selection from the collection tree. If the action instead occured on the new folder button, then create a new folder under the current (single) selection in the collection tree. And finally if the cancel button was pressed, cancel the current, and remaining, jobs on the file queue. */
    public void actionPerformed(ActionEvent event) {
	// If a user has clicked on the bin button directly remove whatever
	// files are selected in the active tree.
      if (event.getSource() == Gatherer.recycle_bin) { 
          if (!Gatherer.recycle_bin.ignore()) { 
		// Find the active tree (you've made selections in).
		DragTree tree = (DragTree) group.getActive();
		if (tree != null) { // if something has indeed been selected
		    // Fudge things a bit
		    group.setSource(tree);
		    // Determine the selection.
		    TreePath paths[] = tree.getSelectionPaths();
		    if(paths != null) {
			FileNode[] source_nodes = new FileNode[paths.length];
			for(int i = 0; i < paths.length; i++) {
			    source_nodes[i] = (FileNode)(paths[i].getLastPathComponent());
			}
			Gatherer.f_man.action(tree, source_nodes, Gatherer.recycle_bin, null);
		    }
		}
	    }
            } 
	// If a user has clicked on new_folder/new_file, create a new folder 
	// or upload a new file under
	// whatever node is selected.
	else if((event.getSource() == new_folder || event.getSource() == new_file) && collection_tree != null) {
	    boolean adding_folder = false; // folder or file?
	    if (event.getSource() == new_folder) {
		adding_folder = true;
	    }
	    int count = collection_tree.getSelectionCount();
	    boolean error = false;
	    if(count == 1) {
		TreePath path = collection_tree.getSelectionPath();
		FileNode node = (FileNode)path.getLastPathComponent();
		if (node.getAllowsChildren()) {
		    if (adding_folder) {
			Gatherer.f_man.newFolder(collection_tree, node);
		    } else {
			Gatherer.f_man.newCollectionFile(collection_tree, node);
		    }
		}
		else {
		    // try the parent
                  FileNode parent = (FileNode) node.getParent();
		    if (parent!=null && parent.getAllowsChildren()) {
			if (adding_folder) {
			    Gatherer.f_man.newFolder(collection_tree, parent);
			} else {
			    Gatherer.f_man.newCollectionFile(collection_tree, parent);
			}
		    } else {
			error = true;
		    }
		}
	    }
	    else {
		error = true;
	    }
	    if(error) {
		// instead of an error, we now create a new folder at the root
              FileNode node = (FileNode) collection_tree.getModel().getRoot();
		if (adding_folder) {
		    Gatherer.f_man.newFolder(collection_tree, node);
		} else {
		    Gatherer.f_man.newCollectionFile(collection_tree, node);
		}
	    }
	}
	else if(event.getSource() == stop_action) {
	    file_queue.cancelAction();
	}
    }


    /** Generates the pane on controls used to 'collect' files into the collection. Resposible for creating, connecting and laying out these controls. */
    public void display()
    {
	// Workspace Tree
	workspace_pane = new JPanel();
	workspace_pane.setMinimumSize(MIN_SIZE);
	workspace_pane.setPreferredSize(TREE_SIZE);
	workspace_pane.setSize(TREE_SIZE);
        workspace_pane.setComponentOrientation(Dictionary.getOrientation());

	workspace_label = new JLabel(Dictionary.get("Collection.Workspace"));
	workspace_label.setOpaque(true);
	workspace_label.setBackground(Configuration.getColor("coloring.workspace_heading_background", false));
	workspace_label.setForeground(Configuration.getColor("coloring.workspace_heading_foreground", false));
	workspace_label.setComponentOrientation(Dictionary.getOrientation());
        
	workspace_tree = new WorkspaceTree(pane_mode);
        workspace_tree.setComponentOrientation(Dictionary.getOrientation());
	group.add(workspace_tree);
	workspace_scroll = new JScrollPane(workspace_tree);
        workspace_scroll.setComponentOrientation(Dictionary.getOrientation());
	workspace_filter = workspace_tree.getFilter();
        workspace_filter.setComponentOrientation(Dictionary.getOrientation());


	// Collection Tree
	collection_pane = new JPanel();
	collection_pane.setMinimumSize(MIN_SIZE);
	collection_pane.setPreferredSize(TREE_SIZE);
	collection_pane.setSize(TREE_SIZE);
        collection_pane.setComponentOrientation(Dictionary.getOrientation());
        
	collection_label = new JLabel(Dictionary.get("Collection.Collection"));
	collection_label.setOpaque(true);
	collection_label.setComponentOrientation(Dictionary.getOrientation());

        if (pane_mode == Utility.IMPORT_MODE) {
          collection_tree = Gatherer.c_man.getCollectionTree();
          collection_tree.setEnabled(Gatherer.c_man.getCollectionTreeModel() != null);
        } else {
          collection_tree = Gatherer.c_man.getFullCollectionTree();
          collection_tree.setEnabled(Gatherer.c_man.getFullCollectionTreeModel() != null);
        }
	
	group.add(collection_tree);
	collection_filter = collection_tree.getFilter();

	tree_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        tree_pane.setComponentOrientation(Dictionary.getOrientation());
	// No collection loaded pane
        if (pane_mode == Utility.IMPORT_MODE) {

          card_layout = new CardLayout();

          // we can have no coll loaded in Gather, but not in Files
          JPanel no_collection_pane = new JPanel();
          no_collection_pane.setBackground(Color.lightGray);
          no_collection_pane.setComponentOrientation(Dictionary.getOrientation());
        
          JLabel no_collection_label = new JLabel(Dictionary.get("Collection.Collection"));
          no_collection_label.setBackground(Color.lightGray);
          no_collection_label.setForeground(Color.black);
          no_collection_label.setOpaque(true);
          no_collection_label.setComponentOrientation(Dictionary.getOrientation());
          
          JPanel no_collection_loaded_panel = new JPanel();
          no_collection_loaded_panel.setBorder(BorderFactory.createLineBorder(Color.black));
          no_collection_loaded_panel.setBackground(Color.lightGray);
          no_collection_loaded_panel.setComponentOrientation(Dictionary.getOrientation());
          
          JLabel no_collection_loaded_label = new JLabel(Dictionary.get("Collection.No_Collection_Loaded"));
          no_collection_loaded_label.setHorizontalAlignment(JLabel.CENTER);
          no_collection_loaded_label.setVerticalAlignment(JLabel.CENTER);
          no_collection_loaded_label.setComponentOrientation(Dictionary.getOrientation());
          card_pane = new JPanel();
          card_pane.setComponentOrientation(Dictionary.getOrientation());
          
          no_collection_loaded_panel.setLayout(new BorderLayout());
          no_collection_loaded_panel.add(no_collection_loaded_label, BorderLayout.CENTER);
          
          no_collection_pane.setLayout(new BorderLayout());
          no_collection_pane.add(no_collection_label, BorderLayout.NORTH);
          no_collection_pane.add(no_collection_loaded_panel, BorderLayout.CENTER);
          
          card_pane.setLayout(card_layout);
          card_pane.add(no_collection_pane, NO_COLLECTION_LOADED_CARD);
          card_pane.add(collection_pane, COLLECTION_LOADED_CARD);
          
        }
	// Status pane
	control_pane = new JPanel();
        control_pane.setComponentOrientation(Dictionary.getOrientation());
        
	JPanel inner_pane = new JPanel();
	inner_pane.setSize(STATUS_SIZE);
        inner_pane.setComponentOrientation(Dictionary.getOrientation());
        
        
	JPanel file_pane = new JPanel();
        file_pane.setComponentOrientation(Dictionary.getOrientation());
        
	JPanel progress_pane = new JPanel();
        progress_pane.setComponentOrientation(Dictionary.getOrientation());
        
	JLabel file_status = file_queue.getFileStatus();
        file_status.setComponentOrientation(Dictionary.getOrientation());
        
	JProgressBar progress_bar = file_queue.getProgressBar();
        progress_bar.setComponentOrientation(Dictionary.getOrientation());
        
	button_pane = new JPanel();
        button_pane.setComponentOrientation(Dictionary.getOrientation());
        RecycleBin recycle_bin = null;
        if (pane_mode == Utility.IMPORT_MODE) {
          recycle_bin = Gatherer.recycle_bin;
          recycle_bin.addActionListener(this);
          recycle_bin.setMinimumSize(MIN_SIZE);
          recycle_bin.setPreferredSize(MIN_SIZE);
          recycle_bin.setToolTipText(Dictionary.get("Collection.Delete_Tooltip"));
          group.add(recycle_bin);
        }
	// Layout Components.
	workspace_pane.setLayout(new BorderLayout());
	workspace_pane.add(workspace_label, BorderLayout.NORTH);
	workspace_pane.add(workspace_scroll, BorderLayout.CENTER);
	workspace_pane.add(workspace_filter, BorderLayout.SOUTH);
	
	collection_pane.setLayout(new BorderLayout());
	collection_pane.add(collection_label, BorderLayout.NORTH);


        if (Dictionary.getOrientation().isLeftToRight()){
            tree_pane.add(workspace_pane, JSplitPane.LEFT);
            if (pane_mode == Utility.IMPORT_MODE) {
              tree_pane.add(card_pane, JSplitPane.RIGHT);
            } else {
              tree_pane.add(collection_pane, JSplitPane.RIGHT);
            }
        }else{
            tree_pane.add(workspace_pane, JSplitPane.RIGHT);
            if (pane_mode== Utility.IMPORT_MODE) {
              tree_pane.add(card_pane, JSplitPane.LEFT);
            } else {
              tree_pane.add(collection_pane, JSplitPane.LEFT);
            }
        }
        
        
	tree_pane.setDividerLocation(TREE_SIZE.width - 10);

	file_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
	file_pane.setLayout(new BorderLayout());
	file_pane.add(file_status, BorderLayout.CENTER);
	file_pane.add(stop_action, BorderLayout.LINE_END);

	progress_pane.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
	progress_pane.setLayout(new BorderLayout());
	progress_pane.add(progress_bar, BorderLayout.CENTER);

	inner_pane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10,10,10,10), BorderFactory.createLoweredBevelBorder()));
	inner_pane.setLayout(new GridLayout(2,1));
	inner_pane.add(file_pane);
	inner_pane.add(progress_pane);

	button_pane.add(new_folder);
	button_pane.add(new_file);
        if (pane_mode == Utility.IMPORT_MODE) {
          button_pane.add(recycle_bin);
        }
	control_pane.setLayout(new BorderLayout());
	control_pane.add(inner_pane, BorderLayout.CENTER);
	control_pane.add(button_pane, BorderLayout.LINE_END);

	this.setLayout(new BorderLayout());
	this.add(tree_pane, BorderLayout.CENTER);
	this.add(control_pane, BorderLayout.SOUTH);

	// For testing, want distinct names for the filter components
	// Want to distinguish collection_filter from workspace_filter (Filter) when testing:
	TestingPreparation.setNamesRecursively(collection_filter, "collection_filter");
    }


    /** Called to inform this control panel that it has just gained focus as an effect of the user clicking on its tab.
     */
    public void gainFocus()
    {
	// Add the collection tree and filter back into this pane
	collection_scroll = new JScrollPane(collection_tree);
	collection_pane.add(collection_scroll, BorderLayout.CENTER);
	collection_pane.add(collection_filter, BorderLayout.SOUTH);
    }


    /** Called to inform this pane that it has just lost focus as an effect of the user clicking on another tab
     */
    public void loseFocus()
    {
	// Remove the collection tree and filter from this pane so it can be added to the Enrich pane
	collection_pane.remove(collection_scroll);
	collection_pane.remove(collection_filter);
    }


    /** Retrieve a list of the currently selected file records in the collection tree. */
    private FileNode[] getCollectionTreeSelection()
    {
	TreePath paths[] = collection_tree.getSelectionPaths();
	FileNode records[] = null;
	if (paths != null) {
	    records = new FileNode[paths.length];
	    for (int i = 0; i < records.length; i++) {
              records[i] = (FileNode)paths[i].getLastPathComponent();
            }
	}
	return records;
    }

    
    /** Called whenever the detail mode changes to ensure the filters are at an appropriate level (ie only editable by those that understand regular expression matching)
     * @param mode the mode level as an int
     */
    public void modeChanged(int mode) {
	collection_filter.setEditable(mode >= Configuration.LIBRARIAN_MODE);
	workspace_filter.setEditable(mode >= Configuration.LIBRARIAN_MODE);
    }


    /** Refresh this pane, depending on what has just happened (refresh_reason). */
    public void refresh(int refresh_reason, boolean collection_loaded)
    {
	if (collection_loaded) {
	    // Update collection tree
	    if (refresh_reason == Gatherer.COLLECTION_OPENED) {
              if(pane_mode == Utility.IMPORT_MODE) {
                card_layout.show(card_pane, COLLECTION_LOADED_CARD);
		collection_tree.setModel(Gatherer.c_man.getCollectionTreeModel());
              } else {
               collection_tree.setModel(Gatherer.c_man.getFullCollectionTreeModel());
              } 
	    }

	}
	else {
	    // Update collection tree
	    collection_tree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Error")));
            if (pane_mode == Utility.IMPORT_MODE) {
              card_layout.show(card_pane, NO_COLLECTION_LOADED_CARD);
            }
	}

	// File sizes turned on/off
	if (refresh_reason == Gatherer.PREFERENCES_CHANGED) {
	    refreshWorkspaceTree(DragTree.TREE_DISPLAY_CHANGED);
	    refreshCollectionTree(DragTree.TREE_DISPLAY_CHANGED);
	}

	// Enable or disable the controls
	collection_tree.setEnabled(collection_loaded);
	collection_filter.setEnabled(collection_loaded);
	new_folder.setEnabled(collection_loaded);
	new_file.setEnabled(collection_loaded);
    }


    public void refreshCollectionTree(int refresh_reason) {
	collection_tree.refresh(null);
    }


    public void refreshWorkspaceTree(int refresh_reason) {
	workspace_tree.refresh(refresh_reason);
    }
}
