/*******************************************************************************
 * Copyright (c) 2007, 2011 Intel 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:
 *     Intel Corporation - initial API and implementation
 *     James Blackburn (Broadcom Corp.)
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.ui.wizards;

import java.util.ArrayList;

import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IProjectType;
import org.eclipse.cdt.managedbuilder.core.IToolChain;
import org.eclipse.cdt.managedbuilder.core.ManagedBuildManager;
import org.eclipse.cdt.managedbuilder.internal.ui.Messages;

/**
 * This class is intended for data exchange between
 * Configuration page and Handlers.
 * It may hold configuration in case of managed project
 * or to be a placeholder in case of make project
 *
 * @noextend This class is not intended to be subclassed by clients.
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
public class CfgHolder {
	private static final String DELIMITER = "_with_"; //$NON-NLS-1$
	private static final String LBR = " (v "; //$NON-NLS-1$
	private static final String RBR = ")"; //$NON-NLS-1$

	private String name;
	private IConfiguration cfg;
	private IToolChain tc;

	public CfgHolder(IToolChain _tc, IConfiguration _cfg) {
		tc = _tc;
		cfg = _cfg;
		if (cfg == null) {
			if (tc == null || tc.getParent() == null)
				name = Messages.StdProjectTypeHandler_2;
			else
				name = tc.getParent().getName();
		} else
			name = cfg.getName();
	}

	/**
	 * @since 8.0
	 */
	public void setConfiguration(IConfiguration cfg) {
		this.cfg = cfg;
	}

	public boolean isSystem() {
		if (cfg == null)
			return false;
		return (cfg.isSystemObject());
	}

	public boolean isSupported() {
		if (cfg == null)
			return true;
		return (cfg.isSupported());
	}

	/**
	 * Checks whether names are unique
	 *
	 * @param its array of CfgHolders
	 * @return boolean if CfgHolder name appears more than once
	 */
	public static boolean hasDoubles(CfgHolder[] its) {
		for (int i = 0; i < its.length; i++) {
			String s = its[i].name;
			for (int j = 0; j < its.length; j++) {
				if (i == j)
					continue;
				if (s.equals(its[j].name))
					return true;
			}
		}
		return false;
	}

	/**
	 * Creates array of {@link CfgHolder}s based on the passed in
	 * {@link IConfiguration}s
	 *
	 * @param cfgs {@link IConfiguration}s to turn to CfgHolders
	 * @return CfgHolder[]
	 */
	public static CfgHolder[] cfgs2items(IConfiguration[] cfgs) {
		CfgHolder[] its = new CfgHolder[cfgs.length];
		for (int i = 0; i < cfgs.length; i++) {
			its[i] = new CfgHolder(cfgs[i].getToolChain(), cfgs[i]);
		}
		return its;
	}

	/**
	 * Makes configuration's names unique.
	 * Adds either version number or toolchain name.
	 * If it does not help, simply adds index.
	 *
	 * @param its - list of items.
	 * @return the same list with unique names.
	 */

	public static CfgHolder[] unique(CfgHolder[] its) {
		// if names are not unique, add version name
		if (hasDoubles(its)) {
			for (int k = 0; k < its.length; k++) {
				if (its[k].tc != null) {
					String ver = ManagedBuildManager.getVersionFromIdAndVersion(its[k].tc.getId());
					if (ver != null)
						its[k].name = its[k].name + LBR + ver + RBR;
				}
			}
		}
		// if names are still not unique, add toolchain name
		if (hasDoubles(its)) {
			for (int k = 0; k < its.length; k++) {
				String s = its[k].name;
				int x = s.indexOf(LBR);
				if (x >= 0)
					s = s.substring(0, x);
				IToolChain tc = its[k].tc;
				if (tc == null && its[k].cfg != null)
					tc = its[k].cfg.getToolChain();
				if (tc != null)
					its[k].name = s + DELIMITER + tc.getUniqueRealName();
			}
		}
		// if names are still not unique, add index
		if (hasDoubles(its)) {
			for (int k = 0; k < its.length; k++) {
				its[k].name = its[k].name + k;
			}
		}
		return its;
	}

	/**
	 * Returns corresponding project type
	 * obtained either from configuration
	 * (if any) or from toolchain.
	 *
	 * @return projectType
	 */

	public IProjectType getProjectType() {
		if (cfg != null)
			return cfg.getProjectType();
		if (tc != null && tc.getParent() != null)
			return tc.getParent().getProjectType();
		return null;
	}

	/**
	 * Reorders selected configurations in "physical" order.
	 * Although toolchains are displayed in alphabetical
	 * order in Wizard, it's required to create corresponding
	 * configurations in the same order as they are listed
	 * in xml file, inside of single project type.
	 *
	 * @param its - items in initial order.
	 * @return - items in "physical" order.
	 */

	public static CfgHolder[] reorder(CfgHolder[] its) {
		ArrayList<CfgHolder> ls = new ArrayList<>(its.length);
		boolean found = true;
		while (found) {
			found = false;
			for (int i = 0; i < its.length; i++) {
				if (its[i] == null)
					continue;
				found = true;
				IProjectType pt = its[i].getProjectType();
				if (pt == null) {
					ls.add(its[i]);
					its[i] = null;
					continue;
				}
				IConfiguration[] cfs = pt.getConfigurations();
				for (int j = 0; j < cfs.length; j++) {
					for (int k = 0; k < its.length; k++) {
						if (its[k] == null)
							continue;
						if (cfs[j].equals(its[k].getTcCfg())) {
							ls.add(its[k]);
							its[k] = null;
						}
					}
				}
			}
		}
		return ls.toArray(new CfgHolder[ls.size()]);
	}

	/*
	 * Note that null configurations are ignored !
	 */
	public static IConfiguration[] items2cfgs(CfgHolder[] its) {
		ArrayList<IConfiguration> lst = new ArrayList<>(its.length);
		for (CfgHolder h : its)
			if (h.cfg != null)
				lst.add(h.cfg);
		return lst.toArray(new IConfiguration[lst.size()]);
	}

	public IConfiguration getTcCfg() {
		if (tc != null)
			return tc.getParent();
		return null;
	}

	/**
	 * @return the IConfiguration stored in this holder.
	 * @since 8.0
	 */
	public IConfiguration getConfiguration() {
		return cfg;
	}

	public String getName() {
		return name;
	}

	/**
	 * @return the IToolchain stored in this holder
	 * @since 8.0
	 */
	public IToolChain getToolChain() {
		return tc;
	}
}
