/*******************************************************************************
 * Copyright (c) 2013, 2014 Ericsson
 *
 * All rights reserved. This program and the accompanying materials are
 * made available under the terms of the Eclipse Public License 2.0 which
 * accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Marc-Andre Laperle - Initial API and implementation
 *******************************************************************************/

package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.importexport;

import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.operation.ModalContext;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.AbstractTracePackageOperation;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.ITracePackageConstants;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageFilesElement;
import org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.TracePackageTraceElement;

/**
 * An operation that extracts information from the manifest located in an
 * archive
 *
 * @author Marc-Andre Laperle
 */
public class TracePackageExtractManifestOperation extends AbstractTracePackageOperation {

    /**
     * Constructs a new import operation for reading the manifest
     *
     * @param fileName
     *            the output file name
     */
    public TracePackageExtractManifestOperation(String fileName) {
        super(fileName);
    }

    /**
     * Run extract the manifest operation. The status (result) of the operation
     * can be obtained with {@link #getStatus}
     *
     * @param progressMonitor
     *            the progress monitor to use to display progress and receive
     *            requests for cancellation
     */
    @Override
    public void run(IProgressMonitor progressMonitor) {
        TracePackageElement[] elements = null;
        try {
            progressMonitor.worked(1);
            ArchiveFile archiveFile = getSpecifiedArchiveFile();
            progressMonitor.worked(1);
            if (archiveFile == null) {
                setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.TracePackageExtractManifestOperation_InvalidFormat));
                return;
            }

            Enumeration<@NonNull ?> entries = archiveFile.entries();

            boolean found = false;
            while (entries.hasMoreElements()) {
                ModalContext.checkCanceled(progressMonitor);

                ArchiveEntry entry = (ArchiveEntry) entries.nextElement();
                IPath p = new Path(entry.getName());
                //Remove project name
                p = p.removeFirstSegments(1);

                if (entry.getName().endsWith(ITracePackageConstants.MANIFEST_FILENAME)) {
                    found = true;
                    InputStream inputStream = archiveFile.getInputStream(entry);
                    ManifestReader.validateManifest(inputStream);

                    inputStream = archiveFile.getInputStream(entry);
                    elements = ManifestReader.loadElementsFromManifest(inputStream);
                    break;
                }

                progressMonitor.worked(1);
            }

            if (found) {
                setStatus(Status.OK_STATUS);
            }
            else {
                elements = generateElementsFromArchive();
                if (elements.length > 0) {
                    setStatus(Status.OK_STATUS);
                } else {
                    setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, MessageFormat.format(Messages.TracePackageExtractManifestOperation_ErrorManifestNotFound, ITracePackageConstants.MANIFEST_FILENAME)));
                }
            }

            setResultElements(elements);
            archiveFile.close();
        } catch (InterruptedException e) {
            setStatus(Status.CANCEL_STATUS);
            Thread.currentThread().interrupt();
        } catch (Exception e) {
            setStatus(new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.TracePackageExtractManifestOperation_ErrorReadingManifest, e));
        }
    }

    private TracePackageElement[] generateElementsFromArchive() {
        ArchiveFile archiveFile = getSpecifiedArchiveFile();
        Enumeration<@NonNull ?> entries = archiveFile.entries();
        Set<String> traceFileNames = new HashSet<>();
        while (entries.hasMoreElements()) {
            ArchiveEntry entry = (ArchiveEntry) entries.nextElement();
            String entryName = entry.getName();
            IPath fullArchivePath = new Path(entryName);
            if (!fullArchivePath.hasTrailingSeparator() && fullArchivePath.segmentCount() > 0) {
                traceFileNames.add(fullArchivePath.segment(0));
            }
        }

        List<TracePackageElement> packageElements = new ArrayList<>();
        for (String traceFileName : traceFileNames) {
            TracePackageTraceElement traceElement = new TracePackageTraceElement(null, traceFileName, null);
            new TracePackageFilesElement(traceElement, traceFileName);
            packageElements.add(traceElement);
        }

        return packageElements.toArray(new TracePackageElement[] {});
    }

}
