/*******************************************************************************
 * Copyright (c) 2005, 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.operations;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.runtime.*;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.listeners.LogEntry;
import org.eclipse.team.internal.ccvs.core.client.listeners.LogListener;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTree;
import org.eclipse.team.internal.ccvs.ui.CVSUIMessages;
import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.ccvs.ui.model.RemoteContentProvider;
import org.eclipse.team.internal.ccvs.ui.operations.RemoteLogOperation.LogEntryCache;
import org.eclipse.team.internal.ccvs.ui.repo.RepositoriesView;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.ui.IWorkbenchPart;

public class FetchAllMembersOperation extends RemoteOperation {
   
	class RLogTreeBuilder {
		    
		    private ICVSRepositoryLocation location;
			private RemoteFolderTree tree;
			private CVSTag tag;

	        public RLogTreeBuilder(ICVSRepositoryLocation location, CVSTag tag) {
	            this.tag = tag;
	            this.location = location;
	            reset();
	        }
			
	        public RemoteFolderTree getTree() {
	            return tree;
	        }
	       
	        /**
	         * Reset the builder to prepare for a new build
	         */
	        public void reset() {
	        	tree = new RemoteFolderTree(null, location, ICVSRemoteFolder.REPOSITORY_ROOT_FOLDER_NAME, tag);
	        	tree.setChildren(new ICVSRemoteResource[0]);
	        }
	        
	        /* (non-Javadoc)
			 * @see org.eclipse.team.internal.ccvs.core.client.listeners.RDiffSummaryListener.IFileDiffListener#newFile(java.lang.String, java.lang.String)
			 */
	        public void newFile(IPath remoteFilePath, ICVSRemoteFile remoteFile) {
				try {
					addFile(tree,tag,remoteFile, remoteFilePath);
				} catch (CVSException e) {
					CVSUIPlugin.log(e);
				} 
			}
			
			private void addFile(RemoteFolderTree tree, CVSTag tag, ICVSRemoteFile file,IPath filePath) throws CVSException {
				RemoteFolderTree parent = (RemoteFolderTree)getFolder(tree, tag, filePath.removeLastSegments(1), Path.EMPTY);
				addChild(parent, file);
			}
			
			private void addChild(RemoteFolderTree tree, ICVSRemoteResource resource) {
				ICVSRemoteResource[] children = tree.getChildren();
				ICVSRemoteResource[] newChildren;
				if (children == null) {
					newChildren = new ICVSRemoteResource[] { resource };
				} else {
					newChildren = new ICVSRemoteResource[children.length + 1];
					System.arraycopy(children, 0, newChildren, 0, children.length);
					newChildren[children.length] = resource;
				}
				tree.setChildren(newChildren);
			}
			
		    /* 
			 * Get the folder at the given path in the given tree, creating any missing folders as needed.
			 */
			private ICVSRemoteFolder getFolder(RemoteFolderTree tree, CVSTag tag, IPath remoteFolderPath, IPath parentPath) throws CVSException {
				if (remoteFolderPath.segmentCount() == 0) return tree;
				String name = remoteFolderPath.segment(0);
				ICVSResource child;
				IPath childPath = parentPath.append(name);
				if (tree.childExists(name)) {
					child = tree.getChild(name);
				}  else {
					child = new RemoteFolderTree(tree, tree.getRepository(), childPath.toString(), tag);
					((RemoteFolderTree)child).setChildren(new ICVSRemoteResource[0]);
					addChild(tree, (ICVSRemoteResource)child);
				}
				return getFolder((RemoteFolderTree)child, tag, remoteFolderPath.removeFirstSegments(1), childPath);
			}
	}
	
	static final String DEAD_STATE = "dead"; //$NON-NLS-1$
	ICVSRepositoryLocation repoLocation;
	
	public FetchAllMembersOperation(IWorkbenchPart part, ICVSRemoteResource[] folders, ICVSRepositoryLocation repoLocation) {
		super(part, folders);
		this.repoLocation = repoLocation;
	}

	@Override
	protected void execute(IProgressMonitor monitor) throws CVSException, InterruptedException {
		//expand each folder selected in the tree
			ICVSRemoteResource[] restest = getRemoteResources();
			ICVSRemoteFolder testfolder = (ICVSRemoteFolder) restest[0];
			CVSTag tag = testfolder.getTag();
			if (tag == null)
				tag = CVSTag.DEFAULT;
			LogEntryCache cache = new LogEntryCache();

			RemoteLogOperation operation = new RemoteLogOperation(getPart(), getRemoteResources(), tag, null, cache);
			try {
				operation.run(monitor);	
				ICVSRemoteResource[] remoteRes = getRemoteResources(); 
			    final ICVSRemoteFolder project = (ICVSRemoteFolder) remoteRes[0];
				//Get the entry paths
				String[] entry = cache.getCachedFilePaths();
				//Strip repo + project info from entries
				RLogTreeBuilder treeBuilder = new RLogTreeBuilder(project.getRepository(),tag);
				for (int i = 0; i < entry.length; i++) {
					ILogEntry[] logEntry = cache.getLogEntries(entry[i]);
					
					//might not have state if this a branch entry
					if (logEntry[0].getState() != null &&
						logEntry[0].getState().equals(DEAD_STATE))
						continue;
					
					
					ICVSRemoteFile remoteFile = logEntry[0].getRemoteFile();
					//if the current folder tag is a branch tag, we need to take the extra step
					//of making sure that the file's revision number has been set appropriately
					if (tag.getType() == CVSTag.BRANCH &&
						remoteFile.getRevision().equals(LogListener.BRANCH_REVISION))
						verifyRevision(tag, logEntry[0], remoteFile);
					
					IPath logPath = new Path(null,remoteFile.getRepositoryRelativePath());
					if (logPath.segmentCount()>0)
						logPath = logPath.removeFirstSegments(1);
				
					treeBuilder.newFile(logPath, remoteFile);
				}
			
				RemoteFolderTree remoteTree = treeBuilder.getTree();
				IWorkbenchPart part = this.getPart();
				if (part instanceof RepositoriesView ){
					final RepositoriesView repView = (RepositoriesView) part;
					RemoteContentProvider prov = repView.getContentProvider();
					prov.addCachedTree(project, remoteTree);
					final TreeViewer tree = repView.getViewer();
				
				Utils.asyncExec((Runnable) () -> tree.expandToLevel(project, AbstractTreeViewer.ALL_LEVELS),
						repView.getViewer());
				}
				
			} catch (InvocationTargetException e) {
				throw CVSException.wrapException(e);
			} catch (InterruptedException e) {
				// Ignore;
			} catch (TeamException e){
				throw CVSException.wrapException(e);
			}
	
	}

	private void verifyRevision(CVSTag tag, ILogEntry entry, ICVSRemoteFile remoteFile) throws CVSException {
		if (entry instanceof LogEntry){
			LogEntry logEntry = (LogEntry) entry;
			String[] allBranchRevisions = logEntry.getBranchRevisions();
			CVSTag[] allCVSTags = entry.getTags();
			for (int i = 0; i < allCVSTags.length; i++) {
				if (allCVSTags[i].equals(tag)){
					//get the revision number stored for this tag
					((RemoteFile) remoteFile).setRevision(allBranchRevisions[i]);
					break;
				}
			}
		}	
	}
	
	@Override
	protected String getTaskName() {
		return CVSUIMessages.FetchAllMembersOperation_0;
	}


}
