/*******************************************************************************
 * Copyright (c) 2000, 2023 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
 *     Joe Bowbeer (jozart@blarg.net) - removed dependency on runtime compatibility layer (bug 74526)
 *******************************************************************************/
package org.eclipse.ui.examples.readmetool;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;

/**
 * Creates the sections used in the <code>ContentOutline</code>
 *
 * @see ReadmeContentOutlinePage#getContentOutline(IAdaptable)
 */
public class ReadmeModelFactory {
	private static ReadmeModelFactory instance = new ReadmeModelFactory();

	private boolean registryLoaded = false;

	IReadmeFileParser parser = null;

	/**
	 * Creates a new ReadmeModelFactory.
	 */
	private ReadmeModelFactory() {
		// do nothing
	}

	/**
	 * Adds all mark elements to the list for the subtree rooted at the given mark
	 * element.
	 */
	protected void addSections(AdaptableList list, MarkElement element) {
		list.add(element);
		Object[] children = element.getChildren(element);
		for (Object child : children) {
			addSections(list, (MarkElement) child);
		}
	}

	/**
	 * Returns the content outline for the given Readme file.
	 *
	 * @param adaptable the element for which to return the content outline
	 * @return the content outline for the argument
	 */
	public AdaptableList getContentOutline(IAdaptable adaptable) {
		return new AdaptableList(getToc((IFile) adaptable));
	}

	/**
	 * Returns the singleton readme adapter.
	 */
	public static ReadmeModelFactory getInstance() {
		return instance;
	}

	/**
	 * Returns a list of all sections in this readme file.
	 *
	 * @param file the file for which to return section heading and subheadings
	 * @return A list containing headings and subheadings
	 */
	public AdaptableList getSections(IFile file) {
		MarkElement[] topLevel = getToc(file);
		AdaptableList list = new AdaptableList();
		for (MarkElement t : topLevel) {
			addSections(list, t);
		}
		return list;
	}

	/**
	 * Convenience method. Looks for a readme file in the selection, and if one is
	 * found, returns the sections for it. Returns null if there is no readme file
	 * in the selection.
	 */
	public AdaptableList getSections(ISelection sel) {
		// If sel is not a structured selection just return.
		if (!(sel instanceof IStructuredSelection))
			return null;
		IStructuredSelection structured = (IStructuredSelection) sel;

		// if the selection is a readme file, get its sections.
		Object object = structured.getFirstElement();
		if (object instanceof IFile file) {
			String extension = file.getFileExtension();
			if (extension != null && extension.equals(IReadmeConstants.EXTENSION)) {
				return getSections(file);
			}
		}

		// the selected object is not a readme file
		return null;
	}

	/**
	 * Parses the contents of the Readme file by looking for lines that start with a
	 * number.
	 *
	 * @param file the file representing the Readme file
	 * @return an element collection representing the table of contents
	 */
	private MarkElement[] getToc(IFile file) {
		if (registryLoaded == false)
			loadParser();
		return parser.parse(file);
	}

	/**
	 * Loads the parser from the registry by searching for extensions that satisfy
	 * our published extension point. For the sake of simplicity, we will pick the
	 * last extension, allowing tools to override what is used. In a more elaborate
	 * tool, all the extensions would be processed.
	 */
	private void loadParser() {
		IExtensionPoint point = Platform.getExtensionRegistry().getExtensionPoint(IReadmeConstants.PLUGIN_ID,
				IReadmeConstants.PP_SECTION_PARSER);
		if (point != null) {
			IExtension[] extensions = point.getExtensions();
			for (int i = 0; i < extensions.length; i++) {
				IExtension currentExtension = extensions[i];
				// in a real application, we would collection
				// the entire list and probably expose it
				// as a drop-down list. For the sake
				// of simplicity, we will pick the last extension only.
				if (i == extensions.length - 1) {
					IConfigurationElement[] configElements = currentExtension.getConfigurationElements();
					for (IConfigurationElement configElement : configElements) {
						if (configElement.getName().equals(IReadmeConstants.TAG_PARSER)) {
							// process the first 'parser' element and stop
							processParserElement(configElement);
							break;
						}
					}
				}
			}
		}
		if (parser == null)
			parser = new DefaultSectionsParser();
		registryLoaded = true;
	}

	/**
	 * Tries to create the Readme file parser. If an error occurs during the
	 * creation of the parser, print an error and set the parser to null.
	 *
	 * @param element the element to process
	 */
	private void processParserElement(IConfigurationElement element) {
		try {
			parser = (IReadmeFileParser) element.createExecutableExtension(IReadmeConstants.ATT_CLASS);
		} catch (CoreException e) {
			// since this is an example just write to the console
			System.out.println(MessageUtil.getString("Unable_to_create_file_parser") + e.getStatus().getMessage()); //$NON-NLS-1$
			parser = null;
		}
	}
}
