/*******************************************************************************
 * Copyright (c) 2007, 2009 Symbian Software Limited 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:
 * Bala Torati (Symbian) - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.core.templateengine.process.processes;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IPathEntry;
import org.eclipse.cdt.core.settings.model.CSourceEntry;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICSourceEntry;
import org.eclipse.cdt.core.settings.model.WriteAccessException;
import org.eclipse.cdt.core.templateengine.TemplateCore;
import org.eclipse.cdt.core.templateengine.process.ProcessArgument;
import org.eclipse.cdt.core.templateengine.process.ProcessFailureException;
import org.eclipse.cdt.core.templateengine.process.ProcessRunner;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;

/**
 * Creates a include Folder to the project.
 */
public class CreateSourceFolder extends ProcessRunner {

	@Override
	public void process(TemplateCore template, ProcessArgument[] args, String processId, IProgressMonitor monitor)
			throws ProcessFailureException {
		createSourceFolder(args[0].getSimpleValue(), args[1].getSimpleValue(), monitor);
	}

	protected void createSourceFolder(String projectName, String targetPath, IProgressMonitor monitor)
			throws ProcessFailureException {
		//If the targetPath is an empty string, there will be no source folder to create.
		// Also this is not an error. So just return gracefully.
		if (targetPath == null || targetPath.length() == 0) {
			return;
		}

		IProject projectHandle = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);

		if (!projectHandle.exists()) {
			throw new ProcessFailureException(Messages.getString("CreateSourceFolder.0") + projectName); //$NON-NLS-1$
		}

		CreateFolder.createFolder(projectName, targetPath, monitor);

		IPath projPath = projectHandle.getFullPath();
		IFolder folder = projectHandle.getFolder(targetPath);

		try {
			ICProject cProject = CoreModel.getDefault().create(projectHandle);
			if (cProject != null) {
				if (CCorePlugin.getDefault().isNewStyleProject(cProject.getProject())) {
					//create source folder for new style project
					createNewStyleProjectFolder(monitor, projectHandle, folder);
				} else {
					//create source folder for all other projects
					createFolder(targetPath, monitor, projPath, cProject);
				}
			}
		} catch (WriteAccessException e) {
			throw new ProcessFailureException(Messages.getString("CreateSourceFolder.2") + e.getMessage(), e); //$NON-NLS-1$
		} catch (CoreException e) {
			throw new ProcessFailureException(Messages.getString("CreateSourceFolder.2") + e.getMessage(), e); //$NON-NLS-1$
		}
	}

	/**
	 * @param monitor
	 * @param projectHandle
	 * @param folder
	 * @throws CoreException
	 * @throws WriteAccessException
	 */
	private void createNewStyleProjectFolder(IProgressMonitor monitor, IProject projectHandle, IFolder folder)
			throws CoreException, WriteAccessException {
		ICSourceEntry newEntry = new CSourceEntry(folder, null, 0);
		ICProjectDescription description = CCorePlugin.getDefault().getProjectDescription(projectHandle);

		ICConfigurationDescription configs[] = description.getConfigurations();
		for (int i = 0; i < configs.length; i++) {
			ICConfigurationDescription config = configs[i];
			ICSourceEntry[] entries = config.getSourceEntries();
			Set<ICSourceEntry> set = new HashSet<>();
			for (int j = 0; j < entries.length; j++) {
				if (new Path(entries[j].getValue()).segmentCount() == 1)
					continue;
				set.add(entries[j]);
			}
			set.add(newEntry);
			config.setSourceEntries(set.toArray(new ICSourceEntry[set.size()]));
		}

		CCorePlugin.getDefault().setProjectDescription(projectHandle, description, false, monitor);
	}

	/**
	 * @param targetPath
	 * @param monitor
	 * @param projPath
	 * @param cProject
	 * @throws CModelException
	 */
	private void createFolder(String targetPath, IProgressMonitor monitor, IPath projPath, ICProject cProject)
			throws CModelException {
		IPathEntry[] entries = cProject.getRawPathEntries();
		List<IPathEntry> newEntries = new ArrayList<>(entries.length + 1);

		int projectEntryIndex = -1;
		IPath path = projPath.append(targetPath);

		for (int i = 0; i < entries.length; i++) {
			IPathEntry curr = entries[i];
			if (path.equals(curr.getPath())) {
				// just return if this folder exists already
				return;
			}
			if (projPath.equals(curr.getPath())) {
				projectEntryIndex = i;
			}
			newEntries.add(curr);
		}

		IPathEntry newEntry = CoreModel.newSourceEntry(path);

		if (projectEntryIndex != -1) {
			newEntries.set(projectEntryIndex, newEntry);
		} else {
			newEntries.add(CoreModel.newSourceEntry(path));
		}

		cProject.setRawPathEntries(newEntries.toArray(new IPathEntry[newEntries.size()]), monitor);
	}

}
