/*******************************************************************************
 * Copyright (c) 2000, 2016 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
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring.utils;

import org.eclipse.cdt.core.CConventions;
import org.eclipse.cdt.core.dom.ast.gnu.cpp.GPPLanguage;
import org.eclipse.cdt.core.dom.parser.AbstractCLikeLanguage;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.corext.util.CModelUtil;
import org.eclipse.cdt.internal.corext.util.Resources;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourceAttributes;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.mapping.IResourceChangeDescriptionFactory;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;

/**
 * This class defines a set of reusable static checks methods.
 */
public class Checks {

	/*
	 * no instances
	 */
	private Checks() {
	}

	public static boolean startsWithUpperCase(String s) {
		if (s == null) {
			return false;
		} else if (s.isEmpty()) {
			return false;
		} else {
			// Workaround for JDK bug (see 26529)
			return s.charAt(0) == Character.toUpperCase(s.charAt(0));
		}
	}

	public static boolean startsWithLowerCase(String s) {
		if (s == null) {
			return false;
		} else if (s.isEmpty()) {
			return false;
		} else {
			// Workaround for JDK bug (see 26529)
			return s.charAt(0) == Character.toLowerCase(s.charAt(0));
		}
	}

	/**
	 * Checks if the given name is a valid Java identifier.
	 *
	 * @param name the java identifier.
	 * @param context an {@link ITranslationUnit} or <code>null</code>
	 * @return a refactoring status containing the error message if the
	 *  name is not a valid java identifier.
	 */
	public static RefactoringStatus checkIdentifier(String name, ITranslationUnit context) {
		return checkName(name, validateIdentifier(name, context));
	}

	public static IStatus validateIdentifier(String name, ITranslationUnit context) {
		ILanguage language = null;
		try {
			if (context != null)
				language = context.getLanguage();
		} catch (CoreException e) {
			// Ignore
		}
		if (language == null) {
			language = GPPLanguage.getDefault();
		}
		if (language instanceof AbstractCLikeLanguage) {
			return CConventions.validateIdentifier(name, (AbstractCLikeLanguage) language);
		}
		return Status.OK_STATUS;
	}

	/**
	 * Returns a fatal error in case the name is empty. In all other cases, an
	 * error based on the given status is returned.
	 *
	 * @param name a name
	 * @param status a status
	 * @return RefactoringStatus based on the given status or the name, if empty.
	 */
	public static RefactoringStatus checkName(String name, IStatus status) {
		RefactoringStatus result = new RefactoringStatus();
		if (name.isEmpty())
			return RefactoringStatus.createFatalErrorStatus(Messages.Checks_choose_name);

		if (status.isOK())
			return result;

		switch (status.getSeverity()) {
		case IStatus.ERROR:
			return RefactoringStatus.createFatalErrorStatus(status.getMessage());
		case IStatus.WARNING:
			return RefactoringStatus.createWarningStatus(status.getMessage());
		case IStatus.INFO:
			return RefactoringStatus.createInfoStatus(status.getMessage());
		default: // Nothing
			return new RefactoringStatus();
		}
	}

	public static boolean resourceExists(IPath resourcePath) {
		return ResourcesPlugin.getWorkspace().getRoot().findMember(resourcePath) != null;
	}

	public static boolean isReadOnly(Object element) throws CModelException {
		if (element instanceof IResource)
			return isReadOnly((IResource) element);

		if (element instanceof ICElement) {
			return isReadOnly(((ICElement) element).getResource());
		}

		Assert.isTrue(false, "Not expected to get here"); //$NON-NLS-1$
		return false;
	}

	public static boolean isReadOnly(IResource res) throws CModelException {
		ResourceAttributes attributes = res.getResourceAttributes();
		if (attributes != null && attributes.isReadOnly())
			return true;

		if (!(res instanceof IContainer))
			return false;

		IContainer container = (IContainer) res;
		IResource[] children;
		try {
			children = container.members();
			for (int i = 0; i < children.length; i++) {
				if (isReadOnly(children[i]))
					return true;
			}
		} catch (CModelException e) {
			throw e;
		} catch (CoreException e) {
			throw new CModelException(e);
		}
		return false;
	}

	//-------- validateEdit checks ----

	public static RefactoringStatus validateModifiesFiles(IFile[] filesToModify, Object context) {
		RefactoringStatus result = new RefactoringStatus();
		IStatus status = Resources.checkInSync(filesToModify);
		if (!status.isOK())
			result.merge(RefactoringStatus.create(status));
		status = Resources.makeCommittable(filesToModify, context);
		if (!status.isOK()) {
			result.merge(RefactoringStatus.create(status));
			if (!result.hasFatalError()) {
				result.addFatalError(Messages.Checks_validate_edit);
			}
		}
		return result;
	}

	public static void addModifiedFilesToChecker(IFile[] filesToModify, CheckConditionsContext context) {
		ResourceChangeChecker checker = context.getChecker(ResourceChangeChecker.class);
		IResourceChangeDescriptionFactory deltaFactory = checker.getDeltaFactory();

		for (int i = 0; i < filesToModify.length; i++) {
			deltaFactory.change(filesToModify[i]);
		}
	}

	public static RefactoringStatus validateEdit(ITranslationUnit tu, Object context) {
		IResource resource = CModelUtil.toOriginal(tu).getResource();
		RefactoringStatus result = new RefactoringStatus();
		if (resource == null)
			return result;
		IStatus status = Resources.checkInSync(resource);
		if (!status.isOK())
			result.merge(RefactoringStatus.create(status));
		status = Resources.makeCommittable(resource, context);
		if (!status.isOK()) {
			result.merge(RefactoringStatus.create(status));
			if (!result.hasFatalError()) {
				result.addFatalError(Messages.Checks_validate_edit);
			}
		}
		return result;
	}
}
