/*******************************************************************************
 * Copyright (c) 2016, 2018 Red Hat.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - Initial Contribution
 *******************************************************************************/

package org.eclipse.linuxtools.internal.docker.core;

import java.util.List;

import org.eclipse.linuxtools.docker.core.IDockerContainer;
import org.eclipse.linuxtools.docker.core.IDockerImage;
import org.eclipse.linuxtools.docker.core.IDockerImageHierarchyNode;

/**
 * Utility class to resolve {@link IDockerImageHierarchyNode}
 */
public class DockerImageHierarchyNodeUtils {

	public static IDockerImageHierarchyNode resolveImageHierarchy(
			final List<IDockerImage> images,
			final List<IDockerContainer> containers, final IDockerImage image) {
		// recursively find all parents and build associated
		// IDockerImageHierarchyNode instances
		final IDockerImageHierarchyNode parentImageNode = getImageParentImageNode(
				images, image.parentId());
		return getDockerImageHierarchyNode(images, containers, image,
				parentImageNode);
	}

	public static IDockerImageHierarchyNode resolveImageHierarchy(
			final List<IDockerImage> images,
			final IDockerContainer container) {
		final IDockerImageHierarchyNode parentImageNode = getContainerParentImageNode(
				images, container.image());
		final DockerImageHierarchyNode dockerImageHierarchyNode = getDockerImageHierarchyNode(
				container, parentImageNode);
		return dockerImageHierarchyNode;
	}

	/**
	 * Resolves the parent {@link IDockerImageHierarchyNode} for an
	 * {@link IDockerImage}.
	 * 
	 * @param images
	 *            all existing {@link IDockerImage}
	 * @param parentImageId
	 *            the id of the parent {@link IDockerImage} to look-up
	 * @return the {@link IDockerImageHierarchyNode} corresponding to the parent
	 *         {@link IDockerImage} to look-up or <code>null</code> if none was
	 *         found.
	 */
	private static IDockerImageHierarchyNode getImageParentImageNode(
			final List<IDockerImage> images, final String parentImageId) {
		// recursively find all parents and build associated
		// IDockerImageHierarchyNode instances
		return images.stream().filter(image -> image.id().equals(parentImageId))
				// parent image found: get its own parent image hierarchy
				.map(parentImage -> new DockerImageHierarchyImageNode(
						parentImage,
						getImageParentImageNode(images,
								parentImage.parentId())))
				.findFirst()
				// no parent image found: stop here.
				.orElse(null);
	}

	/**
	 * Resolves the parent {@link IDockerImageHierarchyNode} for an
	 * {@link IDockerContainer}.
	 * 
	 * @param images
	 *            all existing {@link IDockerImage}
	 * @param parentImageName
	 *            the name of the parent {@link IDockerImage} to look-up
	 * @return the {@link IDockerImageHierarchyNode} corresponding to the parent
	 *         {@link IDockerImage} to look-up or <code>null</code> if none was
	 *         found.
	 */
	private static IDockerImageHierarchyNode getContainerParentImageNode(
			final List<IDockerImage> images, final String parentImageName) {
		// recursively find all parents and build associated
		// IDockerImageHierarchyNode instances
		return images.stream()
				.filter(image -> image.repoTags().contains(parentImageName))
				// parent image found: get its own parent image hierarchy
				.map(parentImage -> new DockerImageHierarchyImageNode(
						parentImage,
						getImageParentImageNode(images, parentImage.parentId())))
				.findFirst()
				// no parent image found: stop here.
				.orElse(null);
	}

	private static DockerImageHierarchyNode getDockerImageHierarchyNode(
			final List<IDockerImage> images,
			final List<IDockerContainer> containers, final IDockerImage image,
			final IDockerImageHierarchyNode parentImageNode) {
		final DockerImageHierarchyNode imageNode = new DockerImageHierarchyImageNode(
				image, parentImageNode);
		// also includes all children images/containers, recursively
		resolveChildrenImageNodes(images, containers, image.id(),
				image.repoTags(), imageNode);
		return imageNode;
	}

	private static void resolveChildrenImageNodes(
			final List<IDockerImage> images,
			final List<IDockerContainer> containers, final String imageId,
			final List<String> imageRepoTags,
			final IDockerImageHierarchyNode parentNode) {
		// recursively find all parents and build associated
		// IDockerImageHierarchyNode instances
		images.stream().filter(image -> image.parentId() != null
				&& image.parentId().equals(imageId))
				// use the flatMap below to duplicate images that have multiple repos
				//.flatMap(image -> DockerImage.duplicateImageByRepo(image))
				.forEach(image -> {
					final DockerImageHierarchyNode childNode = new DockerImageHierarchyImageNode(
							image, parentNode);
					resolveChildrenImageNodes(images, containers, image.id(),
							image.repoTags(),
							childNode);
				});
		containers.stream()
				.filter(container -> container.image() != null
						&& imageRepoTags.contains(container.image()))
				.forEach(container -> new DockerImageHierarchyContainerNode(
						container,
						parentNode));
	}

	private static DockerImageHierarchyNode getDockerImageHierarchyNode(
			final IDockerContainer container,
			final IDockerImageHierarchyNode parentImageNode) {
		// recursively find all parents and build associated
		// IDockerImageHierarchyNode instances
		final DockerImageHierarchyNode containerNode = new DockerImageHierarchyContainerNode(
				container, parentImageNode);
		// there's no children images/containers for a container, so let's just
		// return the node
		return containerNode;
	}

}
