/*******************************************************************************
 * Copyright (c) 2015, 2018 Red Hat Inc. and others.
 * 
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Red Hat - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.man.help;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.help.IToc;
import org.eclipse.help.ITopic;
import org.eclipse.help.IUAElement;
import org.eclipse.linuxtools.internal.man.parser.ManParser;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

/**
 * A table of contents that will have one topic for every manual section that
 * contains at least one installed man page. Each of those topics will have one
 * sub-topic for manual page in the section.
 */
public class ManualToc implements IToc {

	private final Map<String, SectionTopic> sections = new HashMap<>();

	@Override
	public ITopic[] getTopics() {
		if (sections.isEmpty()) {
			generateSections();
		}

		List<SectionTopic> sectionList = new ArrayList<>(sections.values());
		Collections.sort(sectionList);
		return sectionList.toArray(new SectionTopic[sectionList.size()]);
	}

	private void generateSections() {
		// Filter to make sure we only get manual section directories
		DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
			private final Pattern p = Pattern.compile("^man\\d[xp]?"); //$NON-NLS-1$

			@Override
			public boolean accept(Path path) throws IOException {
				Matcher m = p.matcher(path.getFileName().toString());
				return Files.isDirectory(path) && m.matches();
			}
		};

		// Search all man paths in the order that man would, adding sections and
		// pages as we encounter them
		List<Path> manPaths = ManParser.getManPaths();
		for (Path manPath : manPaths) {
			if (Files.notExists(manPath)) {
				continue;
			}
			try (DirectoryStream<Path> manPathStream = Files
					.newDirectoryStream(manPath, filter)) {
				for (Path sectionPath : manPathStream) {
					try (DirectoryStream<Path> sectionPathStream = Files
							.newDirectoryStream(sectionPath)) {
						for (Path pagePath : sectionPathStream) {
							String pageName = pagePath.getFileName().toString();
							if (pageName.endsWith(".gz")) { //$NON-NLS-1$
								pageName = pageName.substring(0,
										pageName.length() - 3);
							}

							// Add page to section
							int dot = pageName.lastIndexOf('.');
							String sectionId = pageName.substring(dot + 1,
									pageName.length());
							String pageId = pageName.substring(0, dot);
							addSectionPage(sectionId, pageId);
						}
					}
				}
			} catch (IOException e) {
				Bundle bundle = FrameworkUtil.getBundle(this.getClass());
				Status status = new Status(IStatus.ERROR, e.getMessage(),
						bundle.getSymbolicName());
				Platform.getLog(bundle).log(status);
			}
		}
	}

	@Override
	public ITopic getTopic(String href) {
		return null;
	}

	@Override
	public boolean isEnabled(IEvaluationContext context) {
		return true;
	}

	@Override
	public IUAElement[] getChildren() {
		return getTopics();
	}

	@Override
	public String getHref() {
		return null;
	}

	@Override
	public String getLabel() {
		return Messages.ManualToc_TocLabel;
	}

	private void addSectionPage(String sectionId, String pageId) {
		StringBuilder label = new StringBuilder();
		String displaySectionId = sectionId;

		// Decide section label
		switch (sectionId.substring(0, 1)) {
		case "0": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section0);
			break;
		case "1": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section1);
			break;
		case "2": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section2);
			break;
		case "3": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section3);
			break;
		case "4": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section4);
			break;
		case "5": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section5);
			break;
		case "6": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section6);
			break;
		case "7": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section7);
			break;
		case "8": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section8);
			break;
		case "9": //$NON-NLS-1$
			label.append(Messages.ManualToc_Section9);
			break;
		}

		// Decide sub-section label
		switch (sectionId.substring(1, sectionId.length())) {
		case "am": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionAM);
			break;
		case "G": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionG);
			break;
		case "p": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionP);
			break;
		case "pm": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionPM);
			break;
		case "python": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionPY);
			break;
		case "x": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionX);
			break;
		case "ssl": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionSSL);
			break;
		case "stap": //$NON-NLS-1$
			label.append(Messages.ManualToc_SectionSTAP);
			break;
		default:
			// If the sub section is not known to us, truncate it and just
			// display the page in the main section
			displaySectionId = sectionId.substring(0, 1);
			break;
		}

		SectionTopic section = sections.get(displaySectionId);
		if (section == null) {
			section = new SectionTopic(displaySectionId, label.toString());
			sections.put(displaySectionId, section);
		}
		section.addPage(sectionId, pageId);
	}
}
