/*******************************************************************************
 * Copyright (c) 2018-2019 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:
 *     Antonio Garcia-Dominguez - initial API and implementation
 ******************************************************************************/
package org.eclipse.hawk.timeaware.tests;

import static org.junit.Assert.assertEquals;

import java.io.File;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.hawk.backend.tests.factories.IGraphDatabaseFactory;
import org.eclipse.hawk.git.JGitRepository;
import org.eclipse.hawk.integration.tests.emf.EMFModelSupportFactory;
import org.eclipse.hawk.integration.tests.mm.Tree.Tree;
import org.eclipse.hawk.integration.tests.mm.Tree.TreeFactory;
import org.eclipse.jgit.api.Git;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

/**
 * Tests for the time-aware indexing of model element nodes, using Git.
 */
@RunWith(Parameterized.class)
public class GitNodeHistoryTest extends AbstractTimeAwareModelIndexingTest {

	@Rule
	public TemporaryFolder folder = new TemporaryFolder();

	@Parameters(name = "{1}")
	public static Iterable<Object[]> params() {
		return TimeAwareTestSuite.caseParams();
	}

	public GitNodeHistoryTest(File baseDir, IGraphDatabaseFactory dbFactory) {
		super(baseDir, dbFactory, new EMFModelSupportFactory());
	}

	@Override
	public void setUp() throws Exception {
		super.setUp();
		Git.init().setDirectory(folder.getRoot()).call();
	}

	@Override
	protected void setUpMetamodels() throws Exception {
		indexer.registerMetamodels(new File(baseDir, TREE_MM_PATH));
	}

	@Test
	public void defaultBranch() throws Throwable {
		final File fTree = new File(folder.getRoot(), "root.xmi");
		final Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath()));

		final Tree tRoot = TreeFactory.eINSTANCE.createTree();
		tRoot.setLabel("root");
		rTree.getContents().add(tRoot);
		rTree.save(null);

		try (Git git = Git.open(folder.getRoot())) {
			git.add().addFilepattern(fTree.getName()).call();
			git.commit().setMessage("first commit").call();

			final Tree tChild = TreeFactory.eINSTANCE.createTree();
			tChild.setLabel("child");
			tRoot.getChildren().add(tChild);
			rTree.save(null);
			git.commit().setAll(true).setMessage("second commit").call();
		}

		JGitRepository vcs = new JGitRepository();
		vcs.init(folder.getRoot().getAbsolutePath(), indexer);
		vcs.run();
		indexer.addVCSManager(vcs, true);

		scheduleAndWait(() -> {
			// 3 versions: beginning of time + 2 commits
			assertEquals(3, timeAwareEOL("return Tree.versions.size;"));
			assertEquals(2, timeAwareEOL("return Tree.latest.all.size;"));
			assertEquals(1, timeAwareEOL("return Tree.latest.prev.all.size;"));
			assertEquals(0, timeAwareEOL("return Tree.latest.prev.prev.all.size;"));
			return null;
		});
	}

	@Test
	public void nonDefaultBranch() throws Throwable {
		final File fTree = new File(folder.getRoot(), "root.xmi");
		final Resource rTree = rsTree.createResource(URI.createFileURI(fTree.getAbsolutePath()));

		final Tree tRoot = TreeFactory.eINSTANCE.createTree();
		tRoot.setLabel("root");
		rTree.getContents().add(tRoot);
		rTree.save(null);

		try (Git git = Git.open(folder.getRoot())) {
			git.add().addFilepattern(fTree.getName()).call();
			git.commit().setMessage("first commit").call();

			final Tree tChild = TreeFactory.eINSTANCE.createTree();
			tChild.setLabel("child");
			tRoot.getChildren().add(tChild);
			rTree.save(null);
			git.commit().setAll(true).setMessage("second commit").call();

			git.checkout().setName("newbranch").setCreateBranch(true).call();
			tChild.setLabel("changed");
			rTree.save(null);
			git.commit().setAll(true).setMessage("third commit in new branch").call();

			git.checkout().setName("master").call();
		}

		JGitRepository vcs = new JGitRepository();
		vcs.init(folder.getRoot().toURI().toString() + "?branch=newbranch", indexer);
		vcs.run();
		indexer.addVCSManager(vcs, true);

		scheduleAndWait(() -> {
			// 3 versions: beginning of time + 2 commits
			assertEquals(1, timeAwareEOL("return Tree.latest.all.select(t|t.label='child').size;"));
			assertEquals("changed", timeAwareEOL("return Tree.latest.all.selectOne(t|t.label='child').latest.label;"));
			assertEquals(2, timeAwareEOL("return Tree.latest.all.selectOne(t|t.label='child').versions.size;"));
			return null;
		});
	}
	
}
