/*******************************************************************************
 * Copyright (c) 2017 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:
 *     Orjuwan Al-Wadeai - Hawk Server Configurator Test
 ******************************************************************************/
package org.eclipse.hawk.service.servlet.config;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
import org.eclipse.hawk.core.IVcsManager;
import org.eclipse.hawk.core.util.IndexedAttributeParameters;
import org.eclipse.hawk.osgiserver.HManager;
import org.eclipse.hawk.osgiserver.HModel;
import org.eclipse.hawk.service.api.utils.APIUtils.ThriftProtocol;
import org.eclipse.hawk.service.servlet.config.ConfigFileParser;
import org.eclipse.hawk.service.servlet.config.HawkInstanceConfig;
import org.eclipse.hawk.service.servlet.config.HawkServerConfigurator;
import org.eclipse.hawk.service.servlet.config.RepositoryParameters;
import org.eclipse.hawk.service.servlet.processors.HawkThriftIface;
import org.junit.BeforeClass;
import org.junit.Test;

import com.google.common.io.Files;

public class HawkServerConfiguratorTest {
	private static final String SERVERCONFIG_PATH = "resources/ServerConfig/";

	private static final String MMDESC_PATH = "resources/metamodel/metamodel_descriptor.xml";
	private static final String ZOO_PATH = "resources/Zoo";

	private static final String INSTANCE1_PATH = "instance_1.xml";
	private static final String INSTANCE2_PATH = "instance_2.xml";

	private static HManager manager;
	private static File configurationFolder;
	private static HawkThriftIface hawkIface;
	private static HawkServerConfigurator serverConfigurator;
	private boolean finishedRetrievingInstanceInfo;

	private Collection<IVcsManager> repos;
	private Collection<IndexedAttributeParameters> derivedAttributes;
	private Collection<IndexedAttributeParameters> indexedAttributes;
	private List<String> metamodels;

	@BeforeClass
	public static void setup() throws IOException {
		initConfigurationFolder();
		setupAndCopyPathsInXmlFile(INSTANCE1_PATH);
		setupAndCopyPathsInXmlFile(INSTANCE2_PATH);

		manager = HManager.getInstance();
		hawkIface = new HawkThriftIface(ThriftProtocol.TUPLE, null, null);
		serverConfigurator = new HawkServerConfigurator(hawkIface);
		serverConfigurator.loadHawkServerConfigurations();
	}

	@Test
	public void testHawkServerConfigurator_instance1() throws Exception {
		testhawkInstance(INSTANCE1_PATH);
	}

	@Test
	public void testHawkServerConfigurator_instance2() throws Exception {
		testhawkInstance(INSTANCE2_PATH);
	}

	private void testhawkInstance(String xmlFileName) throws Exception {
		final ConfigFileParser parser = new ConfigFileParser();
		final HawkInstanceConfig config = parser.parse(new File(configurationFolder, xmlFileName));
		final HModel instance = manager.getHawkByName(config.getName());

		assertTrue(instance.isRunning());
		assertEquals(config.getName(), instance.getName());
		assertEquals(config.getBackend(), instance.getDbType());
		assertArrayEquals(config.getPlugins().toArray(), instance.getEnabledPlugins().toArray());
		assertTrue(hawkIface.listPluginDetails().size() > 0);

		finishedRetrievingInstanceInfo = false;
		instance.configurePolling(0, 0);

		instance.getHawk().getModelIndexer().scheduleTask(new Callable<Void>() {
			@Override
			public Void call() {
				metamodels = instance.getRegisteredMetamodels();
				repos = instance.getRunningVCSManagers();
				derivedAttributes = instance.getDerivedAttributes();
				indexedAttributes = instance.getIndexedAttributes();
				finishedRetrievingInstanceInfo = true;
				return null;
			}
		}, 0);
		;

		while (!finishedRetrievingInstanceInfo) {
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		// repositories
		compareRepositories(config.getRepositories(), repos);

		// metamodels
		assertTrue(metamodels.contains("modelio://ModelioMetaPackage"));
		assertTrue(metamodels.contains("modelio://Modeliosoft.Archimate/1.0.03"));
		assertTrue(metamodels.contains("modelio://Modeliosoft.Analyst/2.0.00"));
		assertTrue(metamodels.contains("modelio://Modeliosoft.modelio.kernel/1.0.00"));
		assertTrue(metamodels.contains("modelio://Modeliosoft.Standard/2.0.00"));
		assertTrue(metamodels.contains("modelio://Modeliosoft.Infrastructure/2.1.00"));

		// derived attributes
		assertTrue(derivedAttributes.size() > 0);
		assertTrue(derivedAttributes.contains(config.getDerivedAttributes().get(0)));

		// indexed attributes
		assertEquals(indexedAttributes.size(), config.getIndexedAttributes().size());
		assertTrue(indexedAttributes.contains(config.getIndexedAttributes().get(0)));
	}

	private void compareRepositories(List<RepositoryParameters> repositories, Collection<IVcsManager> collection) {
		assertEquals(repositories.size(), collection.size());

		for (IVcsManager vcsManager : collection) {
			boolean foundAndEqual = false;

			for (RepositoryParameters repos : repositories) {
				foundAndEqual = foundAndEqual ||
					(vcsManager.getType().equals(repos.getType())
					 && vcsManager.getLocation().equals(repos.getLocation())
					 && vcsManager.isFrozen() == repos.isFrozen()
					 && (vcsManager.getUsername() == null
							? repos.getUser() == null
							: vcsManager.getUsername().equals(repos.getUser()))
					 && (vcsManager.getPassword() == null
							? repos.getPass() == null
							: vcsManager.getPassword().equals(repos.getPass())));
			}

			assertTrue("Should be able to find repo " + vcsManager.getLocation(), foundAndEqual);
		}
	}

	private static void setupAndCopyPathsInXmlFile(String xmlFileName) throws IOException {
		final File fOriginal = new File(SERVERCONFIG_PATH, xmlFileName);
		final File fCopy = new File(configurationFolder, xmlFileName);
		Files.copy(fOriginal, fCopy);

		ConfigFileParser parser = new ConfigFileParser();
		HawkInstanceConfig config = parser.parse(fCopy);
		File location = new File(MMDESC_PATH);
		config.getMetamodels().get(0).setLocation(location.getAbsolutePath());
		config.getRepositories().get(0).setLocation(getLocalFilePath(ZOO_PATH));
		parser.saveConfigAsXml(config);
	}

	private static String getLocalFilePath(String fileLocation) throws IOException {
		String repositoryURI = fileLocation;
		Path path = Paths.get(fileLocation);

		File canonicalFile = null;
		canonicalFile = path.toFile().getCanonicalFile();

		Path rootLocation = canonicalFile.toPath();
		repositoryURI = rootLocation.toUri().toString();

		return repositoryURI;
	}

	private static void initConfigurationFolder() throws IOException {
		final URL installURL = Platform.getConfigurationLocation().getURL();
		final String path = FileLocator.toFileURL(installURL).getPath();
		configurationFolder = new File(path);
	}
}
