/*******************************************************************************
 * Copyright (c) 2017 Red Hat 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:
 *     Mickael Istria (Red Hat Inc.) - initial API and implementation
 *******************************************************************************/
package org.eclipse.ua.tests.doc.internal.linkchecker;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.help.internal.base.BaseHelpSystem;
import org.eclipse.help.internal.search.ISearchHitCollector;
import org.eclipse.help.internal.search.ISearchQuery;
import org.eclipse.help.internal.search.QueryTooComplexException;
import org.eclipse.help.internal.search.SearchHit;
import org.eclipse.help.internal.search.SearchQuery;
import org.eclipse.ui.PlatformUI;

import junit.framework.TestCase;

public class LinkTest extends TestCase {

	public void testAllLinks() {
		ISearchQuery query = new SearchQuery("*", false, Collections.emptyList(), Platform.getNL());
		final Set<URI> indexedPagesURIs = new HashSet<>();
		ISearchHitCollector collector = new ISearchHitCollector() {
			@Override
			public void addQTCException(QueryTooComplexException exception) throws QueryTooComplexException {
				throw exception;
			}

			@Override
			public void addHits(List<SearchHit> hits, String wordsSearched) {
				hits.stream().map(SearchHit::getHref).map(href -> {
					try {
						return PlatformUI.getWorkbench().getHelpSystem().resolve(href, false).toURI();
					} catch (URISyntaxException e) {
						e.printStackTrace();
						return null;
					}
				}).peek(System.err::println).forEach(indexedPagesURIs::add);
			}
		};
		BaseHelpSystem.getSearchManager().search(query, collector, new NullProgressMonitor());
		Set<String> linkFailures = Collections.synchronizedSet(new HashSet<>());
		Set<Exception> ex = Collections.synchronizedSet(new HashSet<>());
		Set<URI> allKnownPageURIs = new HashSet<>(indexedPagesURIs);
		indexedPagesURIs.parallelStream().forEach(t -> {
			try (InputStream stream = t.toURL().openStream()) {
				linkFailures.addAll(checkLinks(stream, t, allKnownPageURIs));
			} catch (IOException e) {
				ex.add(e);
			}
		});
		assertEquals(Collections.emptySet(), ex);
		assertEquals(Collections.emptySet(), linkFailures);
	}

	private Set<String> checkLinks(InputStream stream, URI currentDoc, Set<URI> knownPagesURIs) throws IOException {
		Set<String> res = new HashSet<>();
		try (BufferedReader in = new BufferedReader(new InputStreamReader(stream))) {
			String inputLine;
			while ((inputLine = in.readLine()) != null) {
				int index = 0;
				while ((index = inputLine.indexOf("<a href=\"", index)) > 0) {
					int closeIndex = inputLine.indexOf('"', index + "<a href=\"".length());
					URI href = URI
							.create(inputLine.substring(index + "<a href=\"".length(), closeIndex).replace(" ", "%20"));
					index = closeIndex;
					if (href.isAbsolute()) {
						continue;
					}
					URI linkURI = URI.create(currentDoc.toString() + "/../" + href).normalize();
					if (knownPagesURIs.contains(linkURI)) { // page already indexed or successfully visited
						// we already know this help page exists as it is indexed
						continue;
					} else { // page isn't indexed: can be generated navigation page
						// check whether it's existing anyway
						HttpURLConnection connection = (HttpURLConnection) linkURI.toURL().openConnection();
						connection.setRequestMethod("HEAD");
						connection.connect();
						if (connection.getResponseCode() != 200) {
							res.add("Link from " + currentDoc + " to " + href + " is broken: target URI " + linkURI
									+ " doens't exist.");
						} else {
							knownPagesURIs.add(linkURI);
						}
					}
				}
			}
		}
		return res;
	}

}
