/*******************************************************************************
 * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
 * 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:
 *    Marc R. Hoffmann - initial API and implementation
 *    
 *******************************************************************************/
package org.jacoco.core.test.validation;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Reads a single source file and allows access to it through special probe
 * comments in the following format <code>//$line-<i>tag</i>$.
 */
public class Source {

	/**
	 * Reads the source for the given type from the given source folder relative
	 * to the working directory.
	 * 
	 * @param srcFolder
	 *            source folder
	 * @param type
	 *            type to load the source file for
	 */
	public static Source getSourceFor(final String srcFolder,
			final Class<?> type) throws IOException {
		File folder = new File(srcFolder);
		File file = new File(folder, type.getName().replace('.', '/') + ".java");
		return new Source(new FileReader(file));
	}

	private static final Pattern TAG_PATTERN = Pattern
			.compile("\\$line-(.*)\\$");

	private final List<String> lines = new ArrayList<String>();

	private final Map<String, Integer> tags = new HashMap<String, Integer>();

	/**
	 * Reads a source file from the given reader.
	 * 
	 * @param reader
	 * @throws IOException
	 */
	public Source(final Reader reader) throws IOException {
		final BufferedReader buffer = new BufferedReader(reader);
		for (String l = buffer.readLine(); l != null; l = buffer.readLine()) {
			addLine(l);
		}
		buffer.close();
	}

	private void addLine(final String l) {
		lines.add(l);
		final Matcher m = TAG_PATTERN.matcher(l);
		if (m.find()) {
			final String tag = m.group(1);
			if (tags.put(tag, Integer.valueOf(lines.size())) != null) {
				throw new IllegalArgumentException("Duplicate tag: " + tag);
			}
		}
	}

	/**
	 * Returns all lines of the source file as a list.
	 * 
	 * @return all lines of the source file
	 */
	public List<String> getLines() {
		return Collections.unmodifiableList(lines);
	}

	/**
	 * Returns the line with the given number
	 * 
	 * @param nr
	 *            line number (first line is 1)
	 * @return line content
	 */
	public String getLine(int nr) {
		return lines.get(nr - 1);
	}

	/**
	 * Returns the line number with the given tag
	 * 
	 * @param tag
	 *            tag from a <code>//$line-<i>tag</i>$ marker
	 * @return line number (first line is 1)
	 * @throws NoSuchElementException
	 *             if there is no such tag
	 */
	public int getLineNumber(String tag) throws NoSuchElementException {
		final Integer nr = tags.get(tag);
		if (nr == null) {
			throw new NoSuchElementException("Unknown tag: " + tag);
		}
		return nr.intValue();
	}

}
