/*******************************************************************************
 * Copyright (c) 2010 Peter Stibrany and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Peter Stibrany - initial API and implementation
 *******************************************************************************/

package org.eclipse.mylyn.internal.tasks.ui;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.mylyn.commons.core.ICoreRunnable;
import org.eclipse.mylyn.internal.tasks.ui.util.AttachmentUtil;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskAttachment;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;

/**
 * @author Peter Stibrany
 */
class DownloadAndOpenTaskAttachmentJob implements ICoreRunnable {

	private final ITaskAttachment attachment;

	private final IWorkbenchPage page;

	private final String editorID;

	private final String jobName;

	DownloadAndOpenTaskAttachmentJob(String jobName, ITaskAttachment attachment, IWorkbenchPage page, String editorID) {
		this.jobName = jobName;
		this.attachment = attachment;
		this.page = page;
		this.editorID = editorID;
	}

	public void run(IProgressMonitor monitor) throws CoreException {
		monitor.beginTask(jobName, IProgressMonitor.UNKNOWN);
		try {
			IStatus result = execute(new SubProgressMonitor(monitor, 100));
			if (result != null && !result.isOK()) {
				throw new CoreException(result);
			}
		} finally {
			monitor.done();
		}
	}

	protected IStatus execute(IProgressMonitor monitor) {
		final String attachmentFilename = AttachmentUtil.getAttachmentFilename(attachment);

		File file = null;
		try {
			// create temporary filename like 'attach-127562364-attachment-name.txt'
			// This has correct extension based on attachment filename, resembles attachment name, but
			// also indicates that it is temporary file
			file = File.createTempFile("tmpattach-", "-" + attachmentFilename); //$NON-NLS-1$ //$NON-NLS-2$
		} catch (IOException e) {
			return new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
					Messages.DownloadAndOpenTaskAttachmentJob_failedToDownloadAttachment, e);
		}
		file.deleteOnExit();

		boolean ok = false;
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(file);
			AttachmentUtil.downloadAttachment(attachment, fos, monitor);
			ok = true;

		} catch (IOException e) {
			return new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
					Messages.DownloadAndOpenTaskAttachmentJob_failedToDownloadAttachment, e);
		} catch (CoreException e) {
			int s = IStatus.ERROR;
			if (e.getStatus() != null && e.getStatus().getCode() == IStatus.CANCEL) {
				throw new OperationCanceledException();
			}
			return new Status(s, TasksUiPlugin.ID_PLUGIN,
					Messages.DownloadAndOpenTaskAttachmentJob_failedToDownloadAttachment, e);
		} finally {
			// (fos != null) only when there is some problem, in other cases we nulled fos already
			if (fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					if (ok) {
						file.delete();
						return new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
								Messages.DownloadAndOpenTaskAttachmentJob_failedToDownloadAttachment, e);
					}
				}
			}

			if (!ok) {
				file.delete();
			}
		}

		// mark file read-only to warn user that he is working with local copy
		file.setReadOnly();

		Display disp = page.getWorkbenchWindow().getWorkbench().getDisplay();
		if (disp.isDisposed()) {
			return new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN,
					Messages.DownloadAndOpenTaskAttachmentJob_cannotOpenEditor);
		}

		if (disp.getThread() == Thread.currentThread()) {
			return openEditor(file, attachmentFilename);
		} else {
			final AtomicReference<IStatus> status = new AtomicReference<IStatus>();
			final File tmpFile = file;

			disp.syncExec(new Runnable() {
				public void run() {
					status.set(openEditor(tmpFile, attachmentFilename));
				};
			});

			if (status.get() == null) {
				return new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
						Messages.DownloadAndOpenTaskAttachmentJob_cannotOpenEditor);
			}

			return status.get();
		}
	}

	IStatus openEditor(File file, String attachmentName) {
		try {
			String taskLabel = getTaskLabel(attachment.getTask());
			String repoLabel = getRepositoryLabel(attachment.getTask());

			String tooltip = MessageFormat.format(Messages.DownloadAndOpenTaskAttachmentJob_editorTooltip, taskLabel,
					repoLabel);

			page.openEditor(new AttachmentFileEditorInput(file, attachmentName, tooltip), editorID);
			return Status.OK_STATUS;
		} catch (PartInitException e) {
			return new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN,
					Messages.DownloadAndOpenTaskAttachmentJob_cannotOpenEditor, e);
		}
	}

	private String getTaskLabel(ITask task) {
		AbstractRepositoryConnectorUi connectorUi = TasksUiPlugin.getConnectorUi(task.getConnectorKind());
		StringBuilder taskLabel = new StringBuilder();
		if (connectorUi != null) {
			taskLabel.append(connectorUi.getTaskKindLabel(task));
		}

		String key = task.getTaskKey();
		if (key != null) {
			if (taskLabel.length() > 0) {
				taskLabel.append(" "); //$NON-NLS-1$
			}
			taskLabel.append(key);
		}
		return taskLabel.toString();
	}

	// copied from TaskListToolTip
	private String getRepositoryLabel(ITask task) {
		String repositoryKind = task.getConnectorKind();
		String repositoryUrl = task.getRepositoryUrl();

		TaskRepository repository = TasksUi.getRepositoryManager().getRepository(repositoryKind, repositoryUrl);
		if (repository != null) {
			String label = repository.getRepositoryLabel();
			if (label.indexOf("//") != -1) { //$NON-NLS-1$
				return label.substring((repository.getRepositoryUrl().indexOf("//") + 2)); //$NON-NLS-1$
			}
			return label;
		}
		return ""; //$NON-NLS-1$
	}
}
