/*******************************************************************************
 * Copyright (c) 2000, 2006 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 (int i = 0; i < children.length; ++i) {
            addSections(list, (MarkElement) children[i]);
        }
    }

    /**
     * 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 (int i = 0; i < topLevel.length; i++) {
            addSections(list, topLevel[i]);
        }
        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) {
            IFile file = (IFile) object;
            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 (int j = 0; j < configElements.length; j++) {
                        IConfigurationElement config = configElements[i];
                        if (config.getName()
                                .equals(IReadmeConstants.TAG_PARSER)) {
                            // process the first 'parser' element and stop
                            processParserElement(config);
                            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;
        }
    }
}
