/*******************************************************************************
 * Copyright (c) 2006, 2018 Red Hat Inc. and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Kyu Lee <klee@redhat.com> - initial API and implementation
 *    Jeff Johnston <jjohnstn@redhat.com> - add removed files support
 *******************************************************************************/
package org.eclipse.linuxtools.internal.changelog.core.actions;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;

import org.eclipse.compare.CompareEditorInput;
import org.eclipse.compare.IResourceProvider;
import org.eclipse.compare.ITypedElement;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.linuxtools.internal.changelog.core.ChangeLogExtensionManager;
import org.eclipse.linuxtools.internal.changelog.core.ChangelogPlugin;
import org.eclipse.linuxtools.internal.changelog.core.Messages;
import org.eclipse.team.ui.synchronize.SyncInfoCompareInput;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.ide.FileStoreEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiPageEditorPart;


public abstract class ChangeLogAction extends Action {

    protected ChangeLogExtensionManager extensionManager = null;

    // Preference variables
    protected String pref_AuthorName;

    protected String pref_AuthorEmail;

    protected String pref_ChangeLogName = "ChangeLog"; // $NON-NLS-1$

    protected String pref_Formatter;

    // set selection here
    // selection can be either IEditorPart / IProject / IStructuredSelection
    // IEditorPart and IProject will be adapted to IStructuredSelection.

    public ChangeLogAction() {
        extensionManager = ChangeLogExtensionManager.getExtensionManager();
    }

    protected void reportErr(String msg, Exception e) {
        ChangelogPlugin.getDefault().getLog().log(
                new Status(IStatus.ERROR, ChangelogPlugin.PLUGIN_ID, IStatus.ERROR, msg, e));
    }

    private IWorkspaceRoot getWorkspaceRoot() {
        return ResourcesPlugin.getWorkspace().getRoot();
    }

    protected IEditorPart openEditor(IFile diskresource) {
        IWorkbench ws = PlatformUI.getWorkbench();

        try {
            return org.eclipse.ui.ide.IDE.openEditor(ws
                    .getActiveWorkbenchWindow().getActivePage(), diskresource,
                    true);
        } catch (PartInitException e) {
            e.printStackTrace();

            return null;
        }
    }

    protected IFile createChangeLog(IPath changelog) {
        IWorkspaceRoot myWorkspaceRoot = getWorkspaceRoot();
        IWorkbench ws = PlatformUI.getWorkbench();

        final IFile changelog_File = myWorkspaceRoot.getFile(changelog);
        final InputStream initialContents = new ByteArrayInputStream(
                new byte[0]);

        WorkspaceModifyOperation operation = new WorkspaceModifyOperation() {
            @Override
            public void execute(IProgressMonitor monitor) throws CoreException {
                try {
                    monitor.beginTask(Messages.getString("ChangeLog.AddingChangeLog"), 2000); //$NON-NLS-1$
                    changelog_File.create(initialContents, false, monitor);

                    if (monitor.isCanceled()) {
                        throw new OperationCanceledException();
                    }

                } finally {
                    monitor.done();
                }
            }
        };

        try {
            new ProgressMonitorDialog(ws.getActiveWorkbenchWindow().getShell())
                    .run(true, true, operation);
        } catch (InterruptedException e) {
            reportErr(Messages.getString("ChangeLog.ErrInterrupted"), e); // $NON-NLS-1$
            return null;
        } catch (InvocationTargetException e) {
            reportErr(Messages.getString("ChangeLog.ErrInvocation"), e); // $NON-NLS-1$
            return null;
        }

        // FIXME:  we should put this refreshLocal call into a thread (filed as bug #256180)
        try {
            IContainer changelogContainer = myWorkspaceRoot.getContainerForLocation(changelog);
            if (changelogContainer != null)
                changelogContainer.refreshLocal(2, null);
        } catch (CoreException e) {
            reportErr(Messages.getString("ChangeLog.ErrRefresh"), e); // $NON-NLS-1$
            return null;
        }

        return changelog_File;
    }

    protected IEditorPart askChangeLogLocation(String editorLoc) {
        IWorkbench ws = PlatformUI.getWorkbench();
        IWorkspaceRoot myWorkspaceRoot = getWorkspaceRoot();

        IResource given_resource = myWorkspaceRoot.findMember(editorLoc);

        if (given_resource == null)
            return null;

        ChangeLogContainerSelectionDialog dialog = new ChangeLogContainerSelectionDialog(ws
                .getActiveWorkbenchWindow().getShell(), given_resource
                .getParent(), false, Messages
                .getString("AddAction.str_ChangeLog_Location")); //$NON-NLS-1$
        dialog.showClosedProjects(false);

        dialog.open();

        Object[] result = dialog.getResult();
        if (result == null)
            return null;
        final IPath result_path = new Path(result[0]
                + System.getProperty("file.separator") + pref_ChangeLogName); //$NON-NLS-1$ //$NON-NLS-2$
        IFile newChangeLog = createChangeLog(result_path);

        return openEditor(newChangeLog);

    }

    protected IEditorPart getChangelog(String currentEditorloc) {
        IFile changelog = getChangelogFile(currentEditorloc);
        return changelog != null ? openEditor(changelog) : null;
    }

    protected IFile getChangelogFile(String currentEditorloc) {
        // Scenario 1: The Changelog is in in the current project file
        IWorkspaceRoot myWorkspaceRoot = getWorkspaceRoot();
        IResource given_resource = myWorkspaceRoot.findMember(currentEditorloc);

        if (given_resource != null) {
            IResource parent_dec = given_resource;

            while (parent_dec != null) {
                String parent_node = parent_dec.getFullPath()
                        .removeLastSegments(1).toOSString();
                parent_node = parent_node
                        + System.getProperty("file.separator") + pref_ChangeLogName; //$NON-NLS-1$

                IResource change_log_res = myWorkspaceRoot
                        .findMember(parent_node);

                if (change_log_res != null) {
                    IProject proj_loc = given_resource.getProject();
                    IPath modified_changelog_path = change_log_res
                            .getFullPath().removeFirstSegments(1);
                    IFile change_log_file = proj_loc
                            .getFile(modified_changelog_path);

                    return change_log_file;
                }

                parent_dec = parent_dec.getParent();

                if (parent_dec == null) {
                    break;
                }
            }
        }

        return null;
    }

    /**
     * Find the ChangeLog for a file that is being removed.  It can't be found and
     * it is possible that the directory it is in has also been removed.
     *
     * @param path Path of removed file
     * @return ChangeLog editor part that must be used to report removed file
     */
    protected IEditorPart getChangelogForRemovePath(IPath path) {
        IResource parent_resource = null;
        IPath loc_path = path;
        // Look from current loc up to find first folder that is still existing
        IWorkspaceRoot myWorkspaceRoot = getWorkspaceRoot();
        while (loc_path.segmentCount() > 0) {
            parent_resource = myWorkspaceRoot.findMember(loc_path);
            if (parent_resource != null)
                break;
            loc_path = loc_path.removeLastSegments(1);
        }

        if (parent_resource != null) {
            IResource parent_dec = parent_resource;

            while (parent_dec != null) {
                String parent_node = parent_dec.getFullPath().toOSString();
                parent_node = parent_node
                        + System.getProperty("file.separator") + pref_ChangeLogName; //$NON-NLS-1$

                IResource change_log_res = myWorkspaceRoot
                        .findMember(parent_node);

                if (change_log_res != null) {
                    IProject proj_loc = parent_resource.getProject();
                    IPath modified_changelog_path = change_log_res
                            .getFullPath().removeFirstSegments(1);
                    IFile change_log_file = proj_loc
                            .getFile(modified_changelog_path);

                    return openEditor(change_log_file);
                }

                parent_dec = parent_dec.getParent();

                if (parent_dec == null) {
                    break;
                }
            }
        }

        return null;
    }

    private IFile getDocumentIFile(IEditorPart currentEditor) {
        IEditorInput cc = currentEditor.getEditorInput();

        if (cc instanceof IFileEditorInput) {
            return ((IFileEditorInput) cc).getFile();
        }
        return null;
    }

    protected String getDocumentLocation(IEditorPart currentEditor,
            boolean appendRoot) {


        IFile loc = getDocumentIFile(currentEditor);
        IEditorInput cc = null;
        String WorkspaceRoot;

        IWorkspaceRoot myWorkspaceRoot = getWorkspaceRoot();
        WorkspaceRoot = myWorkspaceRoot.getLocation().toOSString();

        if (currentEditor instanceof MultiPageEditorPart) {
            Object ed = ((MultiPageEditorPart) currentEditor).getSelectedPage();
            if (ed instanceof IEditorPart)
                cc = ((IEditorPart) ed).getEditorInput();
            if (cc instanceof FileEditorInput)
                return (appendRoot) ? WorkspaceRoot + ((FileEditorInput) cc).getFile().getFullPath().toOSString() :
                    ((FileEditorInput) cc).getFile().getFullPath().toOSString();
        }

        cc = currentEditor.getEditorInput();


        if (cc == null)
            return "";

        if ((cc instanceof SyncInfoCompareInput)
                || (cc instanceof CompareEditorInput)) {

            CompareEditorInput test = (CompareEditorInput) cc;
            if (test.getCompareResult() == null) {
                return "";
            } else if (test.getCompareResult() instanceof ICompareInput) {
                ITypedElement leftCompare = ((ICompareInput) test.getCompareResult())
                .getLeft();
                if (leftCompare instanceof IResourceProvider){
                    String localPath = ((IResourceProvider)leftCompare).getResource().getFullPath().toString();
                    if (appendRoot) {
                        return WorkspaceRoot + localPath;
                    }
                    return localPath;
                }
            } else {
                if (appendRoot)
                    return WorkspaceRoot + test.getCompareResult().toString();
                return test.getCompareResult().toString();
            }
        } else if (cc instanceof FileStoreEditorInput) {
            return ((FileStoreEditorInput)cc).getName();
        }



        if (appendRoot) {
            return WorkspaceRoot + loc.getFullPath().toOSString();
        } else if (loc != null) {
            return loc.getFullPath().toOSString();
        } else {
            return "";
        }
    }

    protected void loadPreferences() {
        IPreferenceStore store = ChangelogPlugin.getDefault()
        .getPreferenceStore();

        pref_AuthorName = store.getString("IChangeLogConstants.AUTHOR_NAME"); //$NON-NLS-1$
        pref_AuthorEmail = store.getString("IChangeLogConstants.AUTHOR_EMAIL"); //$NON-NLS-1$

        pref_Formatter = store
                .getString("IChangeLogConstants.DEFAULT_FORMATTER"); // $NON-NLS-1$
    }

}
