/*******************************************************************************
 * Copyright (c) 2011-2018 The University of York, Aston University.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License, v. 2.0 are satisfied: GNU General Public License, version 3.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-3.0
 *
 * Contributors:
 * 	   Nikolas Matragkas, James Williams, Dimitris Kolovos - initial API and implementation
 *     Konstantinos Barmpis - adaptation for use in Hawk
 *     Antonio Garcia-Dominguez - speed up commit item compaction
 ******************************************************************************/
package org.eclipse.hawk.core;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class VcsRepositoryDelta implements Serializable {

	private static final long serialVersionUID = 1L;
	protected IVcsManager vcsManager;
	protected final Iterable<VcsCommit> commits;
	protected String latestRevision;

	public VcsRepositoryDelta(Iterable<VcsCommit> commits) {
		this.commits = commits;
	}

	public IVcsManager getManager() {
		return vcsManager;
	}

	public void setManager(IVcsManager manager) {
		this.vcsManager = manager;
	}

	public Iterable<VcsCommit> getCommits() {
		/*
		 * Using Iterable has a bit of a chicken-and-egg problem:
		 * we cannot create the delta until we have the iterable,
		 * and the items in the iterable cannot know about the delta
		 * since it hasn't been created yet.
		 *
		 * Setting the delta on the fly before returning it seems
		 * to solve the issue.
		 */
		return () -> {
			Iterator<VcsCommit> itCommits = commits.iterator();
			return new Iterator<VcsCommit>() {
				@Override
				public boolean hasNext() {
					return itCommits.hasNext();
				}

				@Override
				public VcsCommit next() {
					VcsCommit commit = itCommits.next();
					commit.setDelta(VcsRepositoryDelta.this);
					return commit;
				}
				
			};
		};
	}

	public int size() {
		int count = 0;
		for (Iterator<VcsCommit> it = commits.iterator(); it.hasNext(); it.next(), count++);
		return count;
	}

	/**
	 * Compacts the information from the various commits. If the same
	 * path has been touched by multiple commits, it will only return
	 * the information from the last commit that changed it.
	 */
	public List<VcsCommitItem> getCompactedCommitItems() {
		final Map<String, VcsCommitItem> compacted = new LinkedHashMap<>();
		for (VcsCommit commit : commits) {
			commit.setDelta(this);

			for (VcsCommitItem item : commit.getItems()) {
				switch (item.getChangeType()) {
				case ADDED:
				case DELETED:
				case UPDATED:
				case REPLACED:
					compacted.put(item.getPath(), item);
					break;
				case UNKNOWN:
					System.err.println("Found unknnown commit kind: " + item.getChangeType());
					break;
				}
			}
		}

		return new ArrayList<>(compacted.values());
	}

}
