/*******************************************************************************
 * Copyright (c) 2006, 2009 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Markus Schorn - initial API and implementation
 *******************************************************************************/ 

package org.eclipse.cdt.internal.core;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.internal.core.pdom.indexer.IndexerPreferences;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeMatcher;
import org.eclipse.core.runtime.content.IContentTypeSettings;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;

/** 
 * Handles the access to the content types of the platform.
 * @author markus.schorn@windriver.com
 */
public class CContentTypes {
	private static final String PREF_LOCAL_CONTENT_TYPE_SETTINGS = "enabled"; //$NON-NLS-1$
	private static final Preferences PROJECT_SCOPE = Platform.getPreferencesService().getRootNode().node(ProjectScope.SCOPE);
	private static final String CONTENT_TYPE_PREF_NODE = "content-types"; //$NON-NLS-1$
	private static final String FULLPATH_CONTENT_TYPE_PREF_NODE = Platform.PI_RUNTIME + IPath.SEPARATOR + CONTENT_TYPE_PREF_NODE;

	/**
	 * Implementation for {@link CCorePlugin#getContentType(IProject, String)}.
	 */
	public static IContentType getContentType(IProject project, String filename) {
		if (filename == null) {
			return null;
		}
		IContentTypeMatcher matcher= null;
		IScopeContext scopeCtx= null;
		boolean preferCpp= true;
		if (project != null) {
			// try with the project settings
			try {
				matcher= project.getContentTypeMatcher();
				if (usesProjectSpecificContentTypes(project)) {
					scopeCtx= new ProjectScope(project);
				}
				if (CoreModel.hasCNature(project)) {
					preferCpp= CoreModel.hasCCNature(project) == IndexerPreferences.preferDefaultLanguage(project);
				}
			} catch (CoreException e) {
				// fallback to workspace wide definitions.
				matcher= Platform.getContentTypeManager();
			}
		}
		else {
			matcher= Platform.getContentTypeManager();
		}

		IContentType[] cts = matcher.findContentTypesFor(filename);
		switch (cts.length) {
		case 0:
			return null;
		case 1:
			return cts[0];
		}
		
		int maxPossiblePriority= scopeCtx == null ? 11 : 101;
		int bestPriority= -1;
		IContentType bestResult= null;
		
		for (int i = 0; i < cts.length; i++) {
			IContentType candidate= cts[i];
			int priority= 0;
			try {
				if (scopeCtx != null) {
					IContentTypeSettings settings= candidate.getSettings(scopeCtx);
					if (isStrictlyAssociatedWith(settings, filename)) {
						priority= 100;
					}
				}
				if (priority == 0 && bestPriority < 100) {
					if (isStrictlyAssociatedWith(candidate, filename)) {
						priority= 10;
					}
				}
				if (isPreferredContentType(candidate, preferCpp)) {
					priority+= 1;
				}
			}
			catch (CoreException e) {
				// skip it
			}
			if (priority > bestPriority) {
				if (priority == maxPossiblePriority) {
					return candidate;
				}
				bestPriority= priority;
				bestResult= candidate;
			}
		}
		return bestResult;
	}
	
	private static boolean isPreferredContentType(IContentType candidate, boolean preferCpp) {
		while (candidate != null) {
			String id= candidate.getId();
			if (CCorePlugin.CONTENT_TYPE_CXXHEADER.equals(id) || 
					CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(id)) {	
				return preferCpp;
			}
			
			if (CCorePlugin.CONTENT_TYPE_CHEADER.equals(id) || 
					CCorePlugin.CONTENT_TYPE_CSOURCE.equals(id)) { 
				return !preferCpp;
			}
			candidate= candidate.getBaseType();
		}
		return false;
	}

	private static boolean isStrictlyAssociatedWith(IContentTypeSettings settings, String filename) {
		String[] namespecs= settings.getFileSpecs(IContentType.FILE_NAME_SPEC);
		for (int i = 0; i < namespecs.length; i++) {
			String name = namespecs[i];
			if (name.equals(filename)) {
				return true;
			}
		}
		// check the file extensions only
		int dotPosition = filename.lastIndexOf('.');
		if (dotPosition >= 0 && dotPosition < filename.length()-1) {
			String fileExtension= filename.substring(dotPosition + 1); 
			String[] extensions= settings.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
			for (int i = 0; i < extensions.length; i++) {
				String ext = extensions[i];
				if (ext.equals(fileExtension)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * This method is copied from the resources plugin and figures out whether
	 * project specific settings are enabled or not.
	 * Implementation for {@link CCorePlugin#usesProjectSpecificContentTypes(IProject)}.
	 */
	public static boolean usesProjectSpecificContentTypes(IProject project) {
		String projectName= project.getName();
		try {
			// be careful looking up for our node so not to create any nodes as side effect
			Preferences node = PROJECT_SCOPE;
			//TODO once bug 90500 is fixed, should be simpler
			// for now, take the long way
			if (!node.nodeExists(projectName))
				return false;
			node = node.node(projectName);
			if (!node.nodeExists(Platform.PI_RUNTIME))
				return false;
			node = node.node(Platform.PI_RUNTIME);
			if (!node.nodeExists(CONTENT_TYPE_PREF_NODE))
				return false;
			node = node.node(CONTENT_TYPE_PREF_NODE);
			return node.getBoolean(PREF_LOCAL_CONTENT_TYPE_SETTINGS, false);
		} catch (BackingStoreException e) {
			// exception treated when retrieving the project preferences
		}
		return false;
	}

	/**
	 * Implementation for {@link CCorePlugin#setUseProjectSpecificContentTypes(IProject, boolean)}. 
	 */
	public static void setUseProjectSpecificContentTypes(IProject project, boolean val) {
		ProjectScope projectScope = new ProjectScope(project);
		Preferences contentTypePrefs = projectScope.getNode(FULLPATH_CONTENT_TYPE_PREF_NODE);
		if (usesProjectSpecificContentTypes(project) != val) {
			// enable project-specific settings for this project
			contentTypePrefs.putBoolean(PREF_LOCAL_CONTENT_TYPE_SETTINGS, val);
			try {
				contentTypePrefs.flush();
			} catch (BackingStoreException e) {
				// ignore ??
			}
		}
	}
}
