/*
 * org.openmicroscopy.shoola.agents.fsimporter.view.ImporterUIElement 
 *
 *------------------------------------------------------------------------------
 *  Copyright (C) 2006-2014 University of Dundee. All rights reserved.
 *
 *
 * 	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.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *------------------------------------------------------------------------------
 */
package org.openmicroscopy.shoola.agents.fsimporter.view;


//Java imports
import info.clearthought.layout.TableLayout;
import info.clearthought.layout.TableLayoutConstraints;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.LineBorder;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.jdesktop.swingx.JXBusyLabel;
import org.openmicroscopy.shoola.agents.events.importer.BrowseContainer;
import org.openmicroscopy.shoola.agents.fsimporter.IconManager;
import org.openmicroscopy.shoola.agents.fsimporter.ImporterAgent;
import org.openmicroscopy.shoola.agents.fsimporter.util.CheckSumDialog;
import org.openmicroscopy.shoola.agents.fsimporter.util.FileImportComponent;
import org.openmicroscopy.shoola.agents.fsimporter.util.ImportStatus;
import org.openmicroscopy.shoola.agents.util.browser.TreeImageDisplay;
import org.openmicroscopy.shoola.env.Environment;
import org.openmicroscopy.shoola.env.LookupNames;
import org.openmicroscopy.shoola.env.data.model.DownloadAndLaunchActivityParam;
import org.openmicroscopy.shoola.env.data.model.ImportableFile;
import org.openmicroscopy.shoola.env.data.model.ImportableObject;
import org.openmicroscopy.shoola.env.data.util.StatusLabel;
import org.openmicroscopy.shoola.env.event.EventBus;
import org.openmicroscopy.shoola.env.ui.UserNotifier;
import org.openmicroscopy.shoola.util.file.ImportErrorObject;
import org.openmicroscopy.shoola.util.ui.ClosableTabbedPaneComponent;
import org.openmicroscopy.shoola.util.ui.RotationIcon;
import org.openmicroscopy.shoola.util.ui.UIUtilities;

import pojos.DataObject;
import pojos.DatasetData;
import pojos.FileAnnotationData;
import pojos.FilesetData;
import pojos.ProjectData;
import pojos.ScreenData;

/** 
 * Component displaying an import.
 *
 * @author Jean-Marie Burel &nbsp;&nbsp;&nbsp;&nbsp;
 * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
 * @author Donald MacDonald &nbsp;&nbsp;&nbsp;&nbsp;
 * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
 * @version 3.0
 * <small>
 * (<b>Internal version:</b> $Revision: $Date: $)
 * </small>
 * @since 3.0-Beta4
 */
class ImporterUIElement 
	extends ClosableTabbedPaneComponent
{
	
	/** Description of the component. */
	private static final String DESCRIPTION = 
		"Closing will cancel imports that have not yet started.";
	
	/** Text indicating to only show the failure.*/
	private static final String SHOW_FAILURE = "Show Failed";
	
	/** Text indicating to show all the imports.*/
	private static final String SHOW_ALL = "Show All";
	
	/** The columns for the layout of the {@link #entries}. */
	private static final double[] COLUMNS = {TableLayout.FILL};
	
	/** The default size of the icon. */
	private static final Dimension ICON_SIZE = new Dimension(16, 16);
	
	/** Icon used when the import is completed successfully. */
	private static final Icon IMPORT_SUCCESS;
	
	/** Icon used when the import failed. */
	private static final Icon IMPORT_FAIL;
	
	/** Icon used when the import is completed. */
	private static final Icon IMPORT_PARTIAL;
	
	static {
		IconManager icons = IconManager.getInstance();
		IMPORT_SUCCESS = icons.getIcon(IconManager.APPLY);
		IMPORT_FAIL = icons.getIcon(IconManager.DELETE);
		IMPORT_PARTIAL = icons.getIcon(IconManager.APPLY_CANCEL);
	}
	
	/** The message to display in the header.*/
	private static final String MESSAGE = 
			"When upload is complete, the import\n" +
			"window and OMERO session can be closed.\n" +
			"Reading will continue on the server.";
	
	/** The object hosting information about files to import. */
	private ImportableObject object;
	
	/** The components to lay out. */
	private LinkedHashMap<String, FileImportComponent>	components;

	/** Component hosting the entries. */
	private JPanel	entries;

	/** The number of cancellation.*/
	private int countCancelled;
	
	/** The number of uploaded files.*/
	private int countUploaded;
	
	/** The number of files/folder imported. */
	private int countImported;
	
	/** The number of files uploaded. */
	private int countUploadFailure;
	
	/** 
	 * The number of failures that occurred during scanning, uploading
	 * or processing.
	 */
	private int countFailure;
	
	/** The total number of files or folder to import. */
	private int totalToImport;
	
	/** The size of the import. */
	private long sizeImport;
	
	/** The component displaying the size the import. */
	private JLabel sizeLabel;
	
	/** The component displaying the number of files to import. */
	private JLabel numberOfImportLabel;

	/** Label for report display */
	private JLabel reportLabel;

	/** Label for import size display */
	private JLabel importSizeLabel;
			
	/** The identifier of the component. */
	private int id;
	
	/** The collection of folders' name used as dataset. */
	private Map<JLabel, Object> foldersName;

	/** Reference to the view. */
	private ImporterUI view;
	
	/** Reference to the controller. */
	private ImporterControl controller;
	
	/** Reference to the controller. */
	private ImporterModel model;

	/** The type of container to handle. */
	private int type;
	
	/** The existing containers. */
	private List<DataObject> existingContainers;

	/** The busy label. */
	private JXBusyLabel busyLabel;
	
	/**The icon used to indicate an on-going import.*/
	private RotationIcon rotationIcon;
	
	/** Controls used to filter the result.*/
	private JButton filterButton;
	
	/** Flag indicating if the upload has started or not.*/
	private boolean uploadStarted;
	
	/**
	 * Returns the object found by identifier.
	 * 
	 * @param data The object to handle.
	 * @param result The collection of element to check.
	 * @return See above.
	 */
	private DataObject getObjectFromID(DataObject data, Collection result)
	{
		Iterator i = result.iterator();
		DataObject object;
		while (i.hasNext()) {
			object = (DataObject) i.next();
			if (object.getClass().equals(data.getClass()) 
					&& object.getId() == data.getId()) {
				return object;
			}
		}
		return null;
	}
	
	/**
	 * Returns the object found by name.
	 * 
	 * @param data The object to handle.
	 * @param result The collection of element to check.
	 * @return See above.
	 */
	private DataObject getObject(DataObject data, Collection result)
	{
		String name = "";
		if (data instanceof ProjectData) {
			name = ((ProjectData) data).getName();
		} else if (data instanceof ScreenData) {
			name = ((ScreenData) data).getName();
		} else if (data instanceof DatasetData) {
			name = ((DatasetData) data).getName();
		}
		Iterator i = result.iterator();
		DataObject object;
		String n = "";
		while (i.hasNext()) {
			object = (DataObject) i.next();
			if (object.getClass().equals(data.getClass())) {
				if (object instanceof ProjectData) {
					n = ((ProjectData) object).getName();
				} else if (object instanceof ScreenData) {
					n = ((ScreenData) object).getName();
				} else if (object instanceof DatasetData) {
					n = ((DatasetData) object).getName();
				}
				if (n.equals(name)) return object;
			}
		}
		return null;
	}
	
	/** Sets the text of indicating the number of imports. */
	private void setNumberOfImport()
	{
		StringBuffer buffer = new StringBuffer();
		int n = countUploaded-countUploadFailure-countCancelled;
		if (n < 0) n = 0;
		buffer.append(n);
		buffer.append(" out of ");
		buffer.append(totalToImport);
		buffer.append(" uploaded");
		numberOfImportLabel.setText(buffer.toString());
	}
	
	/**
	 * Browses the specified object.
	 * 
	 * @param data The object to handle.
	 * @param node The node hosting the object to browse or <code>null</code>.
	 */ 
	private void browse(Object data, Object node)
	{
		EventBus bus = ImporterAgent.getRegistry().getEventBus();
		if (data instanceof TreeImageDisplay || data instanceof DataObject) {
			bus.post(new BrowseContainer(data, node));
		} else if (data instanceof FileImportComponent) {
			FileImportComponent fc = (FileImportComponent) data;
			if (fc.getContainerFromFolder() != null)
				bus.post(new BrowseContainer(fc.getContainerFromFolder(), 
						node));
		}
	}

	/**
	 * Displays only the failures or all the results.
	 */
	private void filterFailures()
	{
		String v = filterButton.getText();
		if (SHOW_FAILURE.equals(v)) {
			filterButton.setText(SHOW_ALL);
			layoutEntries(true);
		} else {
			filterButton.setText(SHOW_FAILURE);
			layoutEntries(false);
		}
	}
	
	/** Initializes the components. */
	private void initialize()
	{
		filterButton = new JButton(SHOW_FAILURE);
		filterButton.setEnabled(false);
		filterButton.addActionListener(new ActionListener() {
			
			/**
			 * Filters or not the import that did not fail.
			 */
			public void actionPerformed(ActionEvent evt) {
				filterFailures();
			}
		});
		sizeImport = 0;
		busyLabel = new JXBusyLabel(ICON_SIZE);
		numberOfImportLabel = UIUtilities.createComponent(null);
		foldersName = new LinkedHashMap<JLabel, Object>();
		countUploadFailure = 0;
		countFailure = 0;
		countUploaded = 0;
		addPropertyChangeListener(controller);
		entries = new JPanel();
		entries.setBackground(UIUtilities.BACKGROUND);
		components = new LinkedHashMap<String, FileImportComponent>();
		List<ImportableFile> files = object.getFiles();
		FileImportComponent c;
		File f;
		Iterator<ImportableFile> i = files.iterator();
		ImportableFile importable;
		type = -1;
		List<Object> containers = object.getRefNodes();
		if (containers != null && containers.size() > 0) {
			Iterator<Object> j = containers.iterator();
			TreeImageDisplay node;
			Object h;
			while (j.hasNext()) {
				node = (TreeImageDisplay) j.next();
				h = node.getUserObject();
				if (h instanceof DatasetData) {
					type = FileImportComponent.DATASET_TYPE;
				} else if (h instanceof ScreenData) {
					type = FileImportComponent.SCREEN_TYPE;
				} else if (h instanceof ProjectData) {
					type = FileImportComponent.PROJECT_TYPE;
				}
				break;
			}
		} else {
			type = FileImportComponent.NO_CONTAINER;
		}
		JLabel l;
		boolean single = model.isSingleGroup();
		while (i.hasNext()) {
			importable = i.next();
			f = (File) importable.getFile();
			c = new FileImportComponent(importable,
					!controller.isMaster(), single, getID(), object.getTags());
			c.setType(type);
			c.addPropertyChangeListener(controller);
			c.addPropertyChangeListener(new PropertyChangeListener() {
				
				public void propertyChange(PropertyChangeEvent evt) {
					String name = evt.getPropertyName();
					if (FileImportComponent.BROWSE_PROPERTY.equals(name)) {
						List<Object> refNodes = object.getRefNodes();
						Object node = null;
						if (refNodes != null && refNodes.size() > 0)
							node = refNodes.get(0);
						browse(evt.getNewValue(), node);
					} else if (
						FileImportComponent.IMPORT_FILES_NUMBER_PROPERTY.equals(
								name)) {
						//-1 to remove the entry for the folder.
						Integer v = (Integer) evt.getNewValue()-1;
						totalToImport += v;
						setNumberOfImport();
					} else if (FileImportComponent.LOAD_LOGFILEPROPERTY.equals(
							name)) {
						FileImportComponent fc = (FileImportComponent)
								evt.getNewValue();
						if (fc == null) return;
						long logFileID = fc.getStatus().getLogFileID();
						if (logFileID <= 0) {
							FilesetData data = fc.getStatus().getFileset();
							if (data == null) return;
							model.fireImportLogFileLoading(data.getId(),
									fc.getIndex());
						} else downloadLogFile(logFileID);
					} else if (
						FileImportComponent.RETRIEVE_LOGFILEPROPERTY.equals(
							name)) {
						FilesetData data = (FilesetData) evt.getNewValue();
						if (data != null)
							model.fireImportLogFileLoading(data.getId(), id);
					} else if (
						FileImportComponent.CHECKSUM_DISPLAY_PROPERTY.equals(
							name)) {
						StatusLabel label = (StatusLabel) evt.getNewValue();
						CheckSumDialog d = new CheckSumDialog(view, label);
						UIUtilities.centerAndShow(d);
					} else if (FileImportComponent.RETRY_PROPERTY.equals(name)) {
						controller.retryUpload(
								(FileImportComponent) evt.getNewValue());
					} else if (
						FileImportComponent.CANCEL_IMPORT_PROPERTY.equals(name)) {
						controller.cancel(
								(FileImportComponent) evt.getNewValue());
					}
				}
			});
			if (f.isDirectory()) {
				if (importable.isFolderAsContainer()) {
					l = new JLabel(f.getName());
					foldersName.put(l, c);
				}
			} else {
				if (importable.isFolderAsContainer()) {
					String name = f.getParentFile().getName();
					//first check if the name is already there.
					Entry<JLabel, Object> entry;
					Iterator<Entry<JLabel, Object>>
					k = foldersName.entrySet().iterator();
					boolean exist = false;
					while (k.hasNext()) {
						entry = k.next();
						l = entry.getKey();
						if (l.getText().equals(name)) {
							exist = true;
							break;
						}
					}
					if (!exist) {
						foldersName.put(new JLabel(name), c);
					}
				}
			}
			importable.setStatus(c.getStatus());
			components.put(c.toString(), c);
		}
		totalToImport = files.size();
	}
	
	/**
	 * Downloads the log file.
	 * 
	 * @param logFileID
	 */
	void downloadLogFile(long logFileID)
	{
		if (logFileID < 0) return;
		Environment env = (Environment) 
				ImporterAgent.getRegistry().lookup(
						LookupNames.ENV);
		String path = env.getOmeroFilesHome();
		File f = new File(path, "importLog_"+logFileID);
		DownloadAndLaunchActivityParam
		activity = new DownloadAndLaunchActivityParam(logFileID,
				DownloadAndLaunchActivityParam.ORIGINAL_FILE,
				f, null);
		activity.setUIRegister(false);
		UserNotifier un =
				ImporterAgent.getRegistry().getUserNotifier();
		un.notifyActivity(model.getSecurityContext(), activity);
	}
	
	/** 
	 * Builds a row.
	 * 
	 * @return See above.
	 */
	private JPanel createRow()
	{
		JPanel p = new JPanel();
		p.setLayout(new FlowLayout(FlowLayout.LEFT));
		p.setBackground(UIUtilities.BACKGROUND_COLOR);
		return p;
	}
	
	/**
	 * Make the JTextArea represent a multiline label.
	 * 
	 * @param textArea The component to handle.
	 */
	private void makeLabelStyle(JTextArea textArea)
	{
		if (textArea == null) return;
		textArea.setEditable(false);
		textArea.setCursor(null);
		textArea.setOpaque(false);
		textArea.setFocusable(false);
	}
	
	/** 
	 * Builds and lays out the header.
	 * 
	 * @return See above.
	 */
	private JPanel buildHeader()
	{
		sizeLabel = UIUtilities.createComponent(null);
		sizeLabel.setText(FileUtils.byteCountToDisplaySize(sizeImport));
		reportLabel = UIUtilities.setTextFont("Report:", Font.BOLD);
		importSizeLabel = UIUtilities.setTextFont("Import Size:", Font.BOLD);
		double[][] design = new double[][]{
					{TableLayout.PREFERRED},
					{TableLayout.PREFERRED, TableLayout.PREFERRED}
				};
		TableLayout layout = new TableLayout(design);
		JPanel detailsPanel = new JPanel(layout);
		detailsPanel.setBackground(UIUtilities.BACKGROUND_COLOR);
		JPanel p = createRow();
		p.add(reportLabel);
		p.add(numberOfImportLabel);
		detailsPanel.add(p, "0, 0");
		p = createRow();
		p.add(importSizeLabel);
		p.add(sizeLabel);
		detailsPanel.add(p, "0, 1");
		
		JPanel middlePanel = new JPanel();
		middlePanel.setBackground(UIUtilities.BACKGROUND_COLOR);
		middlePanel.add(filterButton);
		
    	JTextArea description = new JTextArea(MESSAGE);
    	makeLabelStyle(description);
    	description.setBackground(UIUtilities.BACKGROUND_COLOR);
    	
		JPanel descriptionPanel = new JPanel();
		descriptionPanel.setBackground(UIUtilities.BACKGROUND_COLOR);
		descriptionPanel.add(description);
		
		JPanel header = new JPanel();
		header.setBackground(UIUtilities.BACKGROUND_COLOR);
		header.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
		header.setLayout(new BorderLayout());
		
		header.add(Box.createVerticalStrut(10), BorderLayout.NORTH);
		header.add(detailsPanel, BorderLayout.WEST);
		header.add(middlePanel, BorderLayout.CENTER);
		header.add(descriptionPanel, BorderLayout.EAST);
		header.add(Box.createVerticalStrut(10), BorderLayout.SOUTH);
		
		return header;
	}
	
	/** Builds and lays out the UI. */
	private void buildGUI()
	{
		layoutEntries(false);
		JScrollPane pane = new JScrollPane(entries);
		pane.setOpaque(false);
		pane.setBorder(new LineBorder(Color.LIGHT_GRAY));
		setLayout(new BorderLayout(0, 0));
		add(buildHeader(), BorderLayout.NORTH);
		add(pane, BorderLayout.CENTER);
	}
	
	/** 
	 * Lays out the entries.
	 * 
	 * @param failure Pass <code>true</code> to display the failed import only,
	 * <code>false</code> to display all the entries.
	 */
	private void layoutEntries(boolean failure)
	{
		entries.removeAll();
		TableLayout layout = new TableLayout();
		layout.setColumn(COLUMNS);
		entries.setLayout(layout);
		int index = 0;
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		if (failure) {
			while (i.hasNext()) {
				entry = i.next();
				fc = entry.getValue();
				if (fc.hasComponents()) {
					addRow(layout, index, entry.getValue());
					fc.layoutEntries(failure);
					index++;
				} else {
					if (fc.hasImportFailed()) {
						addRow(layout, index, entry.getValue());
						index++;
					}
				}
			}
		} else {
			while (i.hasNext()) {
				entry = i.next();
				fc = entry.getValue();
				addRow(layout, index, fc);
				fc.layoutEntries(failure);
				index++;
			}
		}
		
		entries.revalidate();
		repaint();
		setNumberOfImport();
	}
	
	/**
	 * Adds a new row.
	 * 
	 * @param layout The layout.
	 * @param index	 The index of the row.
	 * @param c		 The component to add.
	 */
	private void addRow(TableLayout layout, int index, FileImportComponent c)
	{
		layout.insertRow(index, TableLayout.PREFERRED);
		if (index%2 == 0)
			c.setBackground(UIUtilities.BACKGROUND_COLOUR_EVEN);
		else 
			c.setBackground(UIUtilities.BACKGROUND_COLOUR_ODD);
		entries.add(c, new TableLayoutConstraints(0, index));
	}
	
	/**
	 * Creates a new instance.
	 * 
	 * @param controller Reference to the control. Mustn't be <code>null</code>.
	 * @param model Reference to the model. Mustn't be <code>null</code>.
	 * @param view Reference to the model. Mustn't be <code>null</code>.
	 * @param id The identifier of the component.
	 * @param index The index of the component.
	 * @param name The name of the component.
	 * @param object the object to handle. Mustn't be <code>null</code>.
	 */
	ImporterUIElement(ImporterControl controller, ImporterModel model,
			ImporterUI view, int id, int index, String name,
			ImportableObject object)
	{
		super(index, name, DESCRIPTION);
		if (object == null) 
			throw new IllegalArgumentException("No object specified.");
		if (controller == null)
			throw new IllegalArgumentException("No Control.");
		if (model == null)
			throw new IllegalArgumentException("No Model.");
		if (view == null)
			throw new IllegalArgumentException("No View.");
		this.controller = controller;
		this.model = model;
		this.view = view;
		this.id = id;
		this.object = object;
		initialize();
		buildGUI();
	}

	/**
	 * Returns the identifier of the component.
	 * 
	 * @return See above.
	 */
	int getID() { return id; }
	
	/**
	 * Returns the formatted result.
	 * 
	 * @param f The imported file.
	 * @return See above.
	 */
	Object getFormattedResult(ImportableFile f)
	{
		FileImportComponent c = components.get(f.toString());
		if (c == null) return null;
		ImportErrorObject object = c.getImportErrorObject();
		if (object != null) return object;
		
		return null;
	}
	

	/**
	 * Sets the result of the import for the specified file.
	 * 
	 * @param f The imported file.
	 * @param result The result.
	 * @result Returns the formatted result or <code>null</code>.
	 */
	Object uploadComplete(ImportableFile f, Object result)
	{
		FileImportComponent c = components.get(f.toString());
		return uploadComplete(c, result);
	}
	
	/**
	 * Sets the result of the import for the specified file.
	 * 
	 * @param f The imported file.
	 * @param result The result.
	 * @param index The index corresponding to the component
	 * @result Returns the formatted result or <code>null</code>.
	 */
	Object uploadComplete(FileImportComponent c, Object result)
	{
		if (c == null) return null;
		c.uploadComplete(result);
		File file = c.getFile();
		Object r = null;
		if (file.isFile()) {
			countUploaded++;
			sizeImport += c.getImportSize();
			sizeLabel.setText(FileUtils.byteCountToDisplaySize(sizeImport));
			//handle error that occurred during the scanning or upload.
			//Check that the result has not been set.
			//if (!c.hasResult()) {
			if (result instanceof Exception) {
				r = new ImportErrorObject(file, (Exception) result,
						c.getGroupID());
				if (c.hasResult()) return null;
				setImportResult(c, result);
			} else if (result instanceof Boolean) {
				Boolean b = (Boolean) result;
				if (!b && c.isCancelled()) {
					countUploaded--;
					if (isDone() && rotationIcon != null)
						rotationIcon.stopRotation();
				} else
				setImportResult(c, result);
			} else {
				if (c.isCancelled()) {
					if (result == null) {
						countCancelled++;
						countImported++;
						if (isDone() && rotationIcon != null)
							rotationIcon.stopRotation();
					} else {
						countCancelled--;
						countUploaded--;
					}
				}
			}
			//}
		} else {//empty folder
			if (result instanceof Exception) {
				countUploaded++;
				//Check if no files
				if (!c.hasComponents()) {
					countImported++;
					countUploadFailure++;
					c.setStatus(result);
				}
				if (isDone() && rotationIcon != null)
					rotationIcon.stopRotation();
			} else if (result instanceof Boolean) {
				Boolean b = (Boolean) result;
				if (!b && c.isCancelled()) {
					countUploaded++;
					countCancelled++;
					countImported++;
					if (isDone() && rotationIcon != null)
						rotationIcon.stopRotation();
				}
			}
		}
		setNumberOfImport();
		setClosable(isUploadComplete());
		return r;
	}
	
	/**
	 * Sets the result of the import for the specified file.
	 * 
	 * @param fc The component hosting the file to import.
	 * @param result The result.
	 * @result Returns the formatted result or <code>null</code>.
	 */
	void setImportResult(FileImportComponent fc, Object result)
	{
		if (fc == null) return;
		File file = fc.getFile();
		if (file.isFile()) {
			fc.setStatus(result);
			countImported++;
			if (fc.isCancelled() && result != null &&
				!(result instanceof Boolean))
				countImported--;
			if (isDone() && rotationIcon != null)
				rotationIcon.stopRotation();
			if (fc.hasUploadFailed()) {
				countUploadFailure++;
				sizeImport -= fc.getImportSize();
				sizeLabel.setText(FileUtils.byteCountToDisplaySize(sizeImport));
			}
			if (fc.hasImportFailed()) countFailure++;
			setNumberOfImport();
			setClosable(isDone());
			filterButton.setEnabled(countFailure > 0 &&
					countFailure != totalToImport);
		} else { //empty folder
			if (result instanceof Exception) {
				fc.setStatus(result);
				countImported++;
				countFailure++;
				countUploadFailure++;
				if (isDone() && rotationIcon != null)
					rotationIcon.stopRotation();
				setNumberOfImport();
				setClosable(isDone());
			}
		}
	}

	/**
	 * Returns <code>true</code> if the upload is finished, <code>false</code>
	 * otherwise.
	 * 
	 * @return See above.
	 */
	boolean isUploadComplete() { return countUploaded == totalToImport; }
	
	/**
	 * Returns <code>true</code> if the import is finished, <code>false</code>
	 * otherwise.
	 * 
	 * @return See above.
	 */
	boolean isDone() { return countImported == totalToImport; }
	
	/**
	 * Returns <code>true</code> if there is one remaining import,
	 * <code>false</code> otherwise.
	 * 
	 * @return See above.
	 */
	boolean isLastImport() { return countImported == (totalToImport-1); }
	
	/** 
	 * Indicates that the import has started. 
	 * 
	 * @param component The component of reference of the rotation icon.
	 */
	Icon startImport(JComponent component)
	{
		uploadStarted = true;
		setClosable(false);
		busyLabel.setBusy(true);
		repaint();
		return new RotationIcon(busyLabel.getIcon(), component, true);
	}
	
	/**
	 * Returns <code>true</code> if the import has started, <code>false</code>
	 * otherwise.
	 * 
	 * @return See above.
	 */
	boolean hasStarted() { return uploadStarted; }
	
	/**
	 * Manually sets the uploadStarted flag
	 * @param uploadStarted
	 */
	void setUploadStarted(boolean uploadStarted) {
	    this.uploadStarted = uploadStarted;
	}
	
	/**
	 * Returns <code>true</code> if the component has imports in the queue that
	 * have not yet started or been cancelled, <code>false</code> otherwise.
	 * 
	 * @return See above.
	 */
	boolean hasImportToCancel()
	{
	    for (final FileImportComponent fic : components.values()) {
	    	if (!(fic.hasImportStarted() || fic.isCancelled())) {
	            return true;
	        }
	    }
	    return false;
	}

	/**
	 * Returns the collection of files that could not be imported.
	 * 
	 * @return See above.
	 */
	List<FileImportComponent> getMarkedFiles()
	{
		List<FileImportComponent> list = new ArrayList<FileImportComponent>();
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		List<FileImportComponent> l;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			l = fc.getImportErrors();
			if (l != null && l.size() > 0)
				list.addAll(l);
		}
		return list;
	}
	
	/**
	 * Returns the collection of files that could not be imported.
	 * 
	 * @return See above.
	 */
	List<FileImportComponent> getFilesToReupload()
	{
		List<FileImportComponent> list = new ArrayList<FileImportComponent>();
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		List<FileImportComponent> l;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			l = fc.getFilesToReupload();
			if (!CollectionUtils.isEmpty(l))
				list.addAll(l);
		}
		return list;
	}
	
	/**
	 * Returns the object to import.
	 * 
	 * @return See above.
	 */
	ImportableObject getData() { return object; }
	
	/**
	 * Returns the existing containers.
	 * 
	 * @return See above.
	 */
	List<DataObject> getExistingContainers()
	{
		if (existingContainers != null) return existingContainers;
		existingContainers = new ArrayList<DataObject>();
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		Map<Long, DatasetData> datasets = new HashMap<Long, DatasetData>();
		Map<Long, DataObject> projects = new HashMap<Long, DataObject>();
		Map<Long, DataObject> screens = new HashMap<Long, DataObject>();
		DatasetData d;
		DataObject object;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			d = fc.getDataset();
			if (d != null && d.getId() > 0)
				datasets.put(d.getId(), d);
			object = fc.getDataObject();
			if (object instanceof ScreenData && object.getId() > 0)
				screens.put(object.getId(), object);
			if (object instanceof ProjectData && object.getId() > 0) {
				if (d != null && d.getId() <= 0)
					projects.put(object.getId(), object);
			}
		}
		existingContainers.addAll(datasets.values());
		existingContainers.addAll(projects.values());
		existingContainers.addAll(screens.values());
		return existingContainers;
	}

	/**
	 * Sets the import log file for each import component.
	 *
	 * @param data Collection of file annotations linked to the file set.
	 * @param id The id of the file set.
	 */
	void setImportLogFile(Collection<FileAnnotationData> data, long id)
	{
	    if (CollectionUtils.isEmpty(data)) return;
	    Entry<String, FileImportComponent> entry;
	    Iterator<Entry<String, FileImportComponent>>
	    i = components.entrySet().iterator();
	    FileImportComponent fc;
	    Iterator<FileAnnotationData> j;
	    FileAnnotationData fa;
	    while (i.hasNext()) {
	        entry = i.next();
	        fc = entry.getValue();
	        if (fc.getIndex() == id) {
	            j = data.iterator();
	            while (j.hasNext()) {
	                fa = j.next();
	                if (FileAnnotationData.LOG_FILE_NS.equals(
	                        fa.getNameSpace())) {
	                    downloadLogFile(fa.getFileID());
	                    break;
	                }
	            }
	        }
	    }
	}

	/**
	 * Returns <code>true</code> if errors to send, <code>false</code>
	 * otherwise.
	 * 
	 * @return See above.
	 */
	boolean hasFailuresToSend()
	{
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			if (fc.hasFailuresToSend())
				return true;
		}
		return false;
	}
	
	/**
	 * Returns <code>true</code> if files to reimport, <code>false</code>
	 * otherwise.
	 * 
	 * @return See above.
	 */
	boolean hasFailuresToReimport()
	{
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			if (fc.hasFailuresToReimport())
				return true;
		}
		return false;
	}
	
	/**
	 * Returns <code>true</code> if files to re-upload, <code>false</code>
	 * otherwise.
	 * 
	 * @return See above.
	 */
	boolean hasFailuresToReupload()
	{
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			if (fc.hasFailuresToReupload())
				return true;
		}
		return false;
	}
	
	/** Indicates that the import has been cancelled. */
	void cancelLoading()
	{
		if (components == null || components.size() == 0) return;
		Iterator<FileImportComponent> i = components.values().iterator();
		while (i.hasNext()) {
			i.next().cancelLoading();
		}
	}

	/**
	 * Returns <code>true</code> if the view should be refreshed,
	 * <code>false</code> otherwise.
	 * 
	 * @return See above.
	 */
	boolean hasToRefreshTree()
	{
		Entry<String, FileImportComponent> entry;
		Iterator<Entry<String, FileImportComponent>>
		i = components.entrySet().iterator();
		FileImportComponent fc;
		while (i.hasNext()) {
			entry = i.next();
			fc = entry.getValue();
			if (fc.hasToRefreshTree())
				return true;
		}
		return false;
	}
	
	/**
	 * Returns the icon indicating the status of the import.
	 * 
	 * @return See above.
	 */
	Icon getImportIcon()
	{ 
		if (isDone()) {
			Iterator<Entry<String, FileImportComponent>>
			i = components.entrySet().iterator();
			FileImportComponent fc;
			Entry<String, FileImportComponent> entry;
			int failure = 0;
			ImportStatus v;
			while (i.hasNext()) {
				entry = i.next();
				fc = entry.getValue();
				v = fc.getImportStatus();
				if (v == ImportStatus.PARTIAL)
					return IMPORT_PARTIAL;
				if (v == ImportStatus.FAILURE) failure++;
			}
			if (failure == components.size()) return IMPORT_FAIL;
			else if (failure > 0) return IMPORT_PARTIAL;
			return IMPORT_SUCCESS;
		}
		return busyLabel.getIcon();
	}
	
	/** Invokes when the import is finished. */
	void onImportEnded()
	{ 
		busyLabel.setBusy(false);
		setClosable(true);
	}

	/**
	 * Resets the containers in the file to load.
	 * 
	 * @param result The containers to reset.
	 */
	void resetContainers(Collection result)
	{
		if (result == null || result.size() == 0) return;
		List<ImportableFile> files = getData().getFiles();
		if (files == null || files.size() == 0) return;
		Iterator<ImportableFile> i = files.iterator();
		ImportableFile f;
		DataObject parent;
		DatasetData dataset;
		DataObject data;
		ProjectData p;
		DatasetData r;
		while (i.hasNext()) {
			f = i.next();
			parent = f.getParent();
			dataset = f.getDataset();
			if (parent != null) {
				if (parent.getId() <= 0) { //new project or screen
					data = getObject(parent, result);
					r = null;
					if (dataset != null) {
						if (dataset.getId() <= 0) {
							//data is a project
							r = dataset;
							p = (ProjectData) data;
							r = (DatasetData) 
								getObject(dataset, p.getDatasets());
						}
					} 
					f.setLocation(data, r);
				} else { //was already created.
					if (dataset != null) {
						if (dataset.getId() <= 0) {
							data = getObjectFromID(parent, result);
							//data is a project
							r = dataset;
							p = (ProjectData) data;
							r = (DatasetData) 
								getObject(dataset, p.getDatasets());
							f.setLocation(data, r);
						}
					}
				}
			} else { //no parent
				if (dataset != null) {
					if (dataset.getId() <= 0) {
						//data is a project
						r = dataset;
						r = (DatasetData) 
							getObject(dataset, result);
						f.setLocation(null, r);
					}
				}
			}
		}
	}

}
