/*******************************************************************************
 * Copyright (c) 2008, 2012 Nokia 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:
 * Nokia - Initial API and implementation
 *******************************************************************************/

package org.eclipse.cdt.debug.core.executables;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.cdt.core.CCProjectNature;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICOutputEntry;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.internal.core.model.CModelManager;
import org.eclipse.cdt.internal.core.settings.model.CProjectDescriptionManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.debug.core.DebugPlugin;

/**
 * @since 7.0
 */
public class StandardExecutableProvider implements IProjectExecutablesProvider {

	List<String> supportedNatureIds = new ArrayList<>();

	public StandardExecutableProvider() {
		supportedNatureIds.add(CProjectNature.C_NATURE_ID);
		supportedNatureIds.add(CCProjectNature.CC_NATURE_ID);
	}

	@Override
	public List<String> getProjectNatures() {
		return supportedNatureIds;
	}

	@Override
	public List<Executable> getExecutables(IProject project, IProgressMonitor monitor) {
		List<Executable> executables = new ArrayList<>();

		ICProject cproject = CModelManager.getDefault().create(project);
		try {
			// Start out by getting all binaries in all build configurations. If
			// we can't filter based on the active configuration, we'll use this
			// complete list
			IBinary[] allBinaries = cproject.getBinaryContainer().getBinaries();
			if (allBinaries.length == 0) {
				return executables; // save ourselves a lot of pointless busy work
			}

			// Get the output directories of the active build configuration then
			// go through the list of all binaries and pick only the ones that
			// are in these output directories
			List<IBinary> binaries = null;
			ICProjectDescription projDesc = CProjectDescriptionManager.getInstance().getProjectDescription(project,
					false);
			if (projDesc != null) {
				ICConfigurationDescription cfg = projDesc.getActiveConfiguration();
				if (cfg != null) {
					binaries = new ArrayList<>(allBinaries.length);
					ICOutputEntry[] cfgOutDirs = cfg.getBuildSetting().getOutputDirectories();
					for (IBinary allBinary : allBinaries) {
						for (ICOutputEntry outdir : cfgOutDirs) {
							if (outdir.getFullPath().isPrefixOf(allBinary.getPath())) {
								binaries.add(allBinary);
								break;
							}
						}
					}
				}
			}

			// If we weren't able to filter on the active configuration,
			// consider binaries from all configurations
			if (binaries == null) {
				binaries = Arrays.asList(allBinaries);
			}

			SubMonitor progress = SubMonitor.convert(monitor, binaries.size());

			for (IBinary binary : binaries) {
				if (progress.isCanceled()) {
					break;
				}

				if (binary.isExecutable() || binary.isSharedLib()) {
					IPath exePath = binary.getResource().getLocation();
					if (exePath == null)
						exePath = binary.getPath();
					List<ISourceFileRemapping> srcRemappers = new ArrayList<>(2);
					ISourceFileRemappingFactory[] factories = ExecutablesManager.getExecutablesManager()
							.getSourceFileRemappingFactories();
					for (ISourceFileRemappingFactory factory : factories) {
						ISourceFileRemapping remapper = factory.createRemapper(binary);
						if (remapper != null) {
							srcRemappers.add(remapper);
						}
					}
					executables.add(new Executable(exePath, project, binary.getResource(),
							srcRemappers.toArray(new ISourceFileRemapping[srcRemappers.size()])));
				}

				progress.worked(1);
			}
		} catch (CoreException e) {
			CDebugCorePlugin.log(e);
		}

		return executables;
	}

	@Override
	public IStatus removeExecutable(Executable executable, IProgressMonitor monitor) {
		IResource exeResource = executable.getResource();
		if (exeResource != null) {
			try {
				exeResource.delete(true, monitor);
			} catch (CoreException e) {
				DebugPlugin.log(e);
			}
			return Status.OK_STATUS;
		}
		return new Status(IStatus.WARNING, CDebugCorePlugin.PLUGIN_ID, "Can't remove " + executable.getName() //$NON-NLS-1$
				+ ": it is built by project \"" + executable.getProject().getName() + "\""); //$NON-NLS-1$//$NON-NLS-2$
	}
}
