/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 *
 * 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.mappings;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.diff.IDiff;
import org.eclipse.team.core.diff.IThreeWayDiff;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.core.mapping.provider.ResourceDiffTree;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.mapping.CVSCheckedInChangeSet;
import org.eclipse.team.internal.ccvs.ui.CVSUIMessages;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.ccvs.ui.operations.RemoteCompareOperation.CompareTreeBuilder;
import org.eclipse.team.internal.ccvs.ui.subscriber.CVSChangeSetCollector;
import org.eclipse.team.internal.core.mapping.SyncInfoToDiffConverter;
import org.eclipse.team.internal.core.subscribers.ChangeSet;
import org.eclipse.team.internal.core.subscribers.DiffChangeSet;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.team.internal.ui.mapping.ResourceModelParticipantAction;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
import org.eclipse.ui.PlatformUI;

import com.ibm.icu.text.DateFormat;

class OpenChangeSetAction extends ResourceModelParticipantAction {

    protected OpenChangeSetAction(ISynchronizePageConfiguration configuration) {
        super(CVSUIMessages.OpenCommitSetAction_20, configuration);
		ISelection selection = configuration.getSite().getSelectionProvider().getSelection();
		if (selection != null)
			selectionChanged(selection);
    }
    
    private ChangeSet getChangeSet(IStructuredSelection selection) {
        // First, check to see if a change set is selected directly
        if (selection.size() == 1) {
            Object o = selection.getFirstElement();
            if (o instanceof IAdaptable) {
                ChangeSet set = ((IAdaptable)o).getAdapter(ChangeSet.class);
                if (set != null)
                    return set;
            }
        }
        // Failing that, check to see if all the selected elements and their children are in the same change set
        if (selection instanceof TreeSelection) {
			TreeSelection ts = (TreeSelection) selection;
			TreePath[] paths = ts.getPaths();
			if (paths.length > 0) {
				ChangeSet set = getChangeSet(paths[0]);
				if (set != null) {
					for (int i = 1; i < paths.length; i++) {
						TreePath path = paths[i];
						ChangeSet otherSet = getChangeSet(path);
						if (set != otherSet)
							return null;
					}
				}
				return set;
			}
		}
        return null;
    }

	private ChangeSet getChangeSet(TreePath treePath) {
		Object test = treePath.getFirstSegment();
		if (test instanceof ChangeSet) {
			ChangeSet cs = (ChangeSet) test;
			return cs;
		}
		return null;
	}
    
	@Override
	protected boolean isEnabledForSelection(IStructuredSelection selection) {
        // The selection only contains appropriate files so
        // only enable if the selection is contained within a single change set
        ChangeSet set = getChangeSet(selection);
        return set != null;
	}

    public void openEditor(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
        try {
			IDiff[] diffs = getSelectedDiffs(monitor);
			if (diffs.length > 0) {
			    ICVSRepositoryLocation location = getLocation(diffs[0]);
			    if (location == null) {
			        throw new CVSException(CVSUIMessages.OpenCommitSetAction_21); 
			    }
			    CompareTreeBuilder builder = new CompareTreeBuilder(location, null, null);
			    if (buildTrees(builder, diffs)) {
			        builder.cacheContents(monitor);
			        builder.openCompareEditor(getConfiguration().getSite().getPart().getSite().getPage(), getCompareTitle(), getCompareToolTip());
			    }
			}
		} catch (CoreException e) {
			throw new InvocationTargetException(e);
		}
    }
    
    private IDiff[] getSelectedDiffs(IProgressMonitor monitor) throws CoreException {
    	ResourceTraversal[] traversals = getResourceTraversals(getStructuredSelection(), monitor);
    	DiffChangeSet set = (DiffChangeSet)getChangeSet(getStructuredSelection());
		return set.getDiffTree().getDiffs(traversals);
	}

	/*
     * Build the trees that will be compared
     */
    private boolean buildTrees(CompareTreeBuilder builder, IDiff[] diffs) {
    	for (int i = 0; i < diffs.length; i++) {
			IDiff diff = diffs[i];
			if (isFileChange(diff)) {
	            IFileRevision remoteRevision = Utils.getRemote(diff);
	            IResourceVariant remote = SyncInfoToDiffConverter.asResourceVariant(remoteRevision);
	            if (remote == null) {
	                IFileRevision predecessorRevision = Utils.getBase(diff);
	                IResourceVariant predecessor = SyncInfoToDiffConverter.asResourceVariant(predecessorRevision);
	                if (predecessor instanceof ICVSRemoteFile) {
	                    builder.addToTrees((ICVSRemoteFile)predecessor, null);
	                }
	            } else if (remote instanceof ICVSRemoteFile) {
	                try {
	                    ICVSRemoteFile predecessor = getImmediatePredecessor(remote);
	                    builder.addToTrees(predecessor, (ICVSRemoteFile)remote);
	                } catch (TeamException e) {
	                    Utils.handle(e);
	                    return false;
	                }
	            }
			}
        }
        return true;
    }
    
    private boolean isFileChange(IDiff diff) {
		IResource resource = ResourceDiffTree.getResourceFor(diff);
		if (resource.getType() == IResource.FILE) {
			if (diff instanceof IThreeWayDiff) {
				IThreeWayDiff twd = (IThreeWayDiff) diff;
				return twd.getDirection() == IThreeWayDiff.INCOMING || twd.getDirection() == IThreeWayDiff.CONFLICTING;
			}
			return true;
		}
		return false;
	}

	private ICVSRepositoryLocation getLocation(IDiff diff) {
    	IResource resource = ResourceDiffTree.getResourceFor(diff);
    	RepositoryProvider provider = RepositoryProvider.getProvider(resource.getProject());
    	if (provider instanceof CVSTeamProvider) {
			CVSTeamProvider ctp = (CVSTeamProvider) provider;
			try {
				return ctp.getCVSWorkspaceRoot().getRemoteLocation();
			} catch (CVSException e) {
				CVSUIPlugin.log(e);
			}
		}
        return null;
    }
    
    private String getCompareTitle() {
        ChangeSet set = getChangeSet(getStructuredSelection());
        if (set instanceof CVSCheckedInChangeSet) {
        	CVSCheckedInChangeSet cics = (CVSCheckedInChangeSet)set;
            String date = DateFormat.getDateTimeInstance().format(cics.getDate());
            return NLS.bind(CVSUIMessages.OpenChangeSetAction_0, new String[] {cics.getAuthor(), date});
        }
        return CVSUIMessages.OpenChangeSetAction_1;
    }
    
    private String getCompareToolTip() {
    	ChangeSet set = getChangeSet(getStructuredSelection());
    	if (set != null)
    		return set.getName();
    	return null;
    }
    
    private ICVSRemoteFile getImmediatePredecessor(IResourceVariant remote) throws TeamException {
    	CheckedInChangeSetCollector changeSetCollector = getChangeSetCollector();
        if (changeSetCollector != null) {
	        return changeSetCollector.getImmediatePredecessor((ICVSRemoteFile)remote);
        }
        return null;
    }

    private CheckedInChangeSetCollector getChangeSetCollector() {
        return (CheckedInChangeSetCollector)getConfiguration().getProperty(CVSChangeSetCollector.CVS_CHECKED_IN_COLLECTOR);
    }
    
    @Override
	public void run() {
    	try {
			PlatformUI.getWorkbench().getProgressService().run(true, true, monitor -> openEditor(monitor));
		} catch (InvocationTargetException e) {
			Utils.handle(e);
		} catch (InterruptedException e) {
			// Ignore
		}
    }
}
