/*******************************************************************************
 * Copyright (c) 2004, 2015 TimeSys 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:
 *     TimeSys Corporation - Initial implementation
 *     Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.preferences;

import java.util.ArrayList;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.internal.core.model.CModelManager;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
import org.eclipse.core.runtime.content.IContentTypeSettings;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PropertyPage;

/*
 * The preference page used for displaying/editing CDT file
 * type associations for a project
 */
public class CFileTypesPropertyPage extends PropertyPage {

	class FixCFileTypesPreferenceBlock extends CFileTypesPreferenceBlock {
		ArrayList<ContentTypeChangeEvent> list = new ArrayList<>();

		public FixCFileTypesPreferenceBlock() {
			super();
		}

		public FixCFileTypesPreferenceBlock(IProject input) {
			super(input);
		}

		private IContentTypeManager.ContentTypeChangeEvent newContentTypeChangeEvent(IContentType source,
				IScopeContext context) {
			return new IContentTypeManager.ContentTypeChangeEvent(source, context);
		}

		@Override
		public boolean performOk() {
			boolean changed = super.performOk();
			if (changed) {
				int size = list.size();
				if (size > 0) {
					IContentTypeManager.ContentTypeChangeEvent[] events = new IContentTypeManager.ContentTypeChangeEvent[size];
					list.toArray(events);
					CModelManager.getDefault().contentTypeChanged(events);
				}
			}
			return changed;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.ui.preferences.CFileTypesPreferenceBlock#addAssociation(org.eclipse.core.runtime.preferences.IScopeContext, org.eclipse.core.runtime.content.IContentType, java.lang.String, int)
		 */
		@Override
		protected void addAssociation(IScopeContext context, IContentType contentType, String spec, int type) {
			super.addAssociation(context, contentType, spec, type);
			// accumulate the events
			list.add(newContentTypeChangeEvent(contentType, context));
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.internal.ui.preferences.CFileTypesPreferenceBlock#removeAssociation(org.eclipse.core.runtime.preferences.IScopeContext, org.eclipse.core.runtime.content.IContentType, java.lang.String, int)
		 */
		@Override
		protected void removeAssociation(IScopeContext context, IContentType contentType, String spec, int type) {
			super.removeAssociation(context, contentType, spec, type);
			// accumulate the events
			list.add(newContentTypeChangeEvent(contentType, context));
		}

	}

	protected Button fUseWorkspace;
	protected Button fUseProject;
	protected FixCFileTypesPreferenceBlock fPrefsBlock;

	public CFileTypesPropertyPage() {
		super();
		noDefaultAndApplyButton();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.preference.PreferencePage#createContents(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	protected Control createContents(Composite parent) {
		Composite topPane = new Composite(parent, SWT.NONE);

		topPane.setLayout(new GridLayout());
		topPane.setLayoutData(new GridData(GridData.FILL_BOTH));

		// Workspace radio buttons

		Composite radioPane = new Composite(topPane, SWT.NONE);

		radioPane.setLayout(new GridLayout());
		radioPane.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		fUseWorkspace = new Button(radioPane, SWT.RADIO);
		fUseWorkspace.setText(PreferencesMessages.CFileTypesPropertyPage_useWorkspaceSettings);
		fUseWorkspace.addListener(SWT.Selection, new Listener() {
			@Override
			public void handleEvent(Event e) {
				if (fUseWorkspace.getSelection()) {
					fPrefsBlock.setInput(null);
					fPrefsBlock.setEnabled(false);
				}
			}
		});

		final IProject project = getProject();
		boolean custom = CCorePlugin.usesProjectSpecificContentTypes(project);

		fUseProject = new Button(radioPane, SWT.RADIO);
		fUseProject.setText(PreferencesMessages.CFileTypesPropertyPage_useProjectSettings);
		fUseProject.addListener(SWT.Selection, new Listener() {
			@Override
			public void handleEvent(Event e) {
				if (fUseProject.getSelection()) {
					fPrefsBlock.setInput(project);
					fPrefsBlock.setEnabled(true);
				}
			}
		});

		Composite blockPane = new Composite(topPane, SWT.NONE);

		blockPane.setLayout(new GridLayout());
		blockPane.setLayoutData(new GridData(GridData.FILL_BOTH));

		if (custom) {
			fPrefsBlock = new FixCFileTypesPreferenceBlock(project);
		} else {
			fPrefsBlock = new FixCFileTypesPreferenceBlock();
		}

		fPrefsBlock.createControl(blockPane);

		fUseWorkspace.setSelection(!custom);
		fUseProject.setSelection(custom);
		fPrefsBlock.setEnabled(custom);

		PlatformUI.getWorkbench().getHelpSystem().setHelp(topPane, ICHelpContextIds.FILE_TYPES_STD_PAGE);
		return topPane;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.preference.PreferencePage#performDefaults()
	 */
	@Override
	protected void performDefaults() {
		fUseWorkspace.setSelection(true);
		fUseProject.setSelection(false);
		fPrefsBlock.setInput(null);
		fPrefsBlock.setEnabled(false);
		super.performDefaults();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.preference.IPreferencePage#performOk()
	 */
	@Override
	public boolean performOk() {
		boolean useProjectContentTypes = fUseProject.getSelection();
		IProject project = getProject();
		if (CCorePlugin.usesProjectSpecificContentTypes(project) != useProjectContentTypes) {
			CCorePlugin.setUseProjectSpecificContentTypes(project, useProjectContentTypes);
			computeEvents(project);
		}
		if (useProjectContentTypes) {
			fPrefsBlock.performOk();
		}
		return super.performOk();
	}

	private IProject getProject() {
		Object element = getElement();
		IProject project = null;

		if (element instanceof IProject) {
			project = (IProject) element;
		} else if (element instanceof IAdaptable) {
			project = ((IAdaptable) element).getAdapter(IProject.class);
		}
		return project;
	}

	public IContentType[] getRegistedContentTypes() {
		String[] ids = CoreModel.getRegistedContentTypeIds();
		IContentTypeManager manager = Platform.getContentTypeManager();
		IContentType[] ctypes = new IContentType[ids.length];
		for (int i = 0; i < ids.length; i++) {
			ctypes[i] = manager.getContentType(ids[i]);
		}
		return ctypes;
	}

	/**
	 * We are looking at the association from the project, if we have any
	 * we should generate an event.
	 * @param project
	 */
	void computeEvents(IProject project) {
		IScopeContext projectScope = new ProjectScope(project);
		IContentType[] ctypes = getRegistedContentTypes();
		ArrayList<IContentType> list = new ArrayList<>(ctypes.length);
		for (IContentType ctype : ctypes) {
			try {
				IContentTypeSettings projectSettings = ctype.getSettings(projectScope);
				String[] projectSpecs = projectSettings.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
				if (projectSpecs != null && projectSpecs.length > 0) {
					list.add(ctype);
				} else {
					projectSpecs = projectSettings.getFileSpecs(IContentType.FILE_NAME_SPEC);
					if (projectSpecs != null && projectSpecs.length > 0) {
						list.add(ctype);
					}
				}
			} catch (CoreException e) {
				// ignore ?
			}
		}

		// fire the events
		if (list.size() > 0) {
			IContentTypeManager.ContentTypeChangeEvent[] events = new IContentTypeManager.ContentTypeChangeEvent[list
					.size()];
			for (int i = 0; i < list.size(); ++i) {
				IContentType source = list.get(i);
				events[i] = new IContentTypeManager.ContentTypeChangeEvent(source, projectScope);
			}
			CModelManager.getDefault().contentTypeChanged(events);
		}
	}

	boolean isSpecsChanged(String[] newSpecs, String[] oldSpecs) {
		if (newSpecs.length != oldSpecs.length) {
			return true;
		}
		for (int i = 0; i < newSpecs.length; ++i) {
			String newSpec = newSpecs[i];
			boolean found = false;
			for (int j = 0; j < oldSpecs.length; ++j) {
				String oldSpec = oldSpecs[j];
				if (newSpec.equalsIgnoreCase(oldSpec)) {
					found = true;
					break;
				}
			}
			if (!found) {
				return true;
			}
		}
		return false;
	}

}
