/*******************************************************************************
 * Copyright (c) 2000, 2020 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
 *     Alexander Fedorov (ArSysOp) - Bug 561712
 *******************************************************************************/
package org.eclipse.core.runtime;

import java.util.*;

/**
 * A concrete multi-status implementation, suitable either for instantiating or
 * subclassing.
 * <p>
 * This class can be used without OSGi running.
 * </p>
 */
public class MultiStatus extends Status {

	/**
	 * List of child statuses.
	 */
	private final List<IStatus> children = new ArrayList<>();

	/**
	 * Creates and returns a new multi-status object with the given children.
	 *
	 * @param caller      the relevant class to build unique identifier from
	 * @param code        the caller-specific status code
	 * @param newChildren the list of children status objects
	 * @param message     a human-readable message, localized to the current locale
	 * @param exception   a low-level exception, or <code>null</code> if not
	 *                    applicable
	 * 
	 * @since 3.12
	 */
	public MultiStatus(Class<?> caller, int code, IStatus[] newChildren, String message, Throwable exception) {
		this(caller, code, message, exception);
		Assert.isLegal(newChildren != null);
		addAllInternal(newChildren);
	}

	/**
	 * Creates and returns a new multi-status object with the given children.
	 *
	 * @param pluginId    the unique identifier of the relevant plug-in
	 * @param code        the plug-in-specific status code
	 * @param newChildren the list of children status objects
	 * @param message     a human-readable message, localized to the current locale
	 * @param exception   a low-level exception, or <code>null</code> if not
	 *                    applicable
	 */
	public MultiStatus(String pluginId, int code, IStatus[] newChildren, String message, Throwable exception) {
		this(pluginId, code, message, exception);
		Assert.isLegal(newChildren != null);
		addAllInternal(newChildren);
	}

	/**
	 * Creates and returns a new multi-status object with no children.
	 *
	 * @param caller    the relevant class to build unique identifier from
	 * @param code      the caller-specific status code
	 * @param message   a human-readable message, localized to the current locale
	 * @param exception a low-level exception, or <code>null</code> if not
	 *                  applicable
	 * 
	 * @since 3.12
	 */
	public MultiStatus(Class<?> caller, int code, String message, Throwable exception) {
		super(OK, caller, code, message, exception);
	}

	/**
	 * Creates and returns a new multi-status object with no children.
	 *
	 * @param pluginId  the unique identifier of the relevant plug-in
	 * @param code      the plug-in-specific status code
	 * @param message   a human-readable message, localized to the current locale
	 * @param exception a low-level exception, or <code>null</code> if not
	 *                  applicable
	 */
	public MultiStatus(String pluginId, int code, String message, Throwable exception) {
		super(OK, pluginId, code, message, exception);
	}

	/**
	 * Creates and returns a new multi-status object with no children.
	 *
	 * @param caller  the relevant class to build unique identifier from
	 * @param code    the caller-specific status code
	 * @param message a human-readable message, localized to the current locale
	 * 
	 * @since 3.12
	 */
	public MultiStatus(Class<?> caller, int code, String message) {
		super(OK, caller, code, message, null);
	}

	/**
	 * Creates and returns a new multi-status object with no children.
	 *
	 * @param pluginId the unique identifier of the relevant plug-in
	 * @param code     the plug-in-specific status code
	 * @param message  a human-readable message, localized to the current locale
	 * @since 3.11
	 */
	public MultiStatus(String pluginId, int code, String message) {
		super(OK, pluginId, code, message, null);
	}

	/**
	 * Adds the given status to this multi-status.
	 *
	 * @param status the new child status
	 */
	public void add(IStatus status) {
		Assert.isLegal(status != null);
		children.add(status);
		int newSev = status.getSeverity();
		if (newSev > getSeverity()) {
			setSeverity(newSev);
		}
	}

	/**
	 * Adds all of the children of the given status to this multi-status. Does
	 * nothing if the given status has no children (which includes the case where it
	 * is not a multi-status).
	 *
	 * @param status the status whose children are to be added to this one
	 */
	public void addAll(IStatus status) {
		Assert.isLegal(status != null);
		addAllInternal(status.getChildren());
	}

	private void addAllInternal(IStatus[] newChildren) {
		int maxSeverity = getSeverity();
		for (IStatus child : newChildren) {
			Assert.isLegal(child != null);
			int severity = child.getSeverity();
			if (severity > maxSeverity)
				maxSeverity = severity;
		}
		this.children.addAll(Arrays.asList(newChildren));
		setSeverity(maxSeverity);
	}

	@Override
	public IStatus[] getChildren() {
		return children.toArray(new IStatus[0]);
	}

	@Override
	public boolean isMultiStatus() {
		return true;
	}

	/**
	 * Merges the given status into this multi-status. Equivalent to
	 * <code>add(status)</code> if the given status is not a multi-status.
	 * Equivalent to <code>addAll(status)</code> if the given status is a
	 * multi-status.
	 *
	 * @param status the status to merge into this one
	 * @see #add(IStatus)
	 * @see #addAll(IStatus)
	 */
	public void merge(IStatus status) {
		Assert.isLegal(status != null);
		if (!status.isMultiStatus()) {
			add(status);
		} else {
			addAll(status);
		}
	}

	/**
	 * Returns a string representation of the status, suitable for debugging
	 * purposes only.
	 */
	@Override
	public String toString() {
		StringJoiner joiner = new StringJoiner(" ", super.toString() + " children=[", "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		for (IStatus child : children) {
			joiner.add(child.toString());
		}
		return joiner.toString();
	}
}
