/*
 * Copyright 2020 ETSI
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package fr.mines_stetienne.ci.saref.checkers;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import fr.mines_stetienne.ci.saref.SAREFPipelineException;
import fr.mines_stetienne.ci.saref.managers.RepositoryManager;

/**
 * Checks TS 103 673 Clause 9.2: Structure of the SAREF project version directory
 * 
 */
public class Clause_9_2_Checker extends AbstractClauseChecker {

	private static enum MESSAGE {
		requirements, ontology, tests, examples, documentation, readme_missing, gitignore_missing, gitignore_incomplete, gitignore_ioexception, 
	    license_missing, license_firstline, license_year, license_line, license_overcomplete, 
	    license_incomplete, license_ioexception, securityexception;
	}

	private static final Set<String> gitignoreLines = new HashSet<>(
			Arrays.asList("target", "*~", ".DS_Store", "catalog-v001.xml", "saref-pipeline.jar"));

	public Clause_9_2_Checker(RepositoryManager repositoryManager) {
		super(repositoryManager, Clause_9_2_Checker.class);
	}

	@Override
	public void checkClause() throws SAREFPipelineException {
		checkReadme();
		checkGitignore();
		checkLICENSE();
		checkDirectory("requirements", MESSAGE.requirements);
		checkDirectory("ontology", MESSAGE.ontology);
		checkDirectory("tests", MESSAGE.tests);
		checkDirectory("examples", MESSAGE.examples);
		checkDirectory("documentation", MESSAGE.documentation);
	}

	private void checkReadme() {
		final File file = new File(repository.getDirectory(), "README.md");
		if (file.isFile()) {
			return;
		}
		logError(getMessage(MESSAGE.readme_missing));
	}

	private void checkGitignore() throws SAREFPipelineException {
		final File file = new File(repository.getDirectory(), ".gitignore");
		if (!file.isFile()) {
			logError(getMessage(MESSAGE.gitignore_missing));
			return;
		}
		try (BufferedReader br = Files.newBufferedReader(file.toPath())) {
			for (String line = br.readLine(); line != null; line = br.readLine()) {
				gitignoreLines.remove(line);
			}
			if (!gitignoreLines.isEmpty()) {
				logError(getMessage(MESSAGE.gitignore_incomplete, gitignoreLines.toString()));
			}
		} catch (IOException ex) {
			logError(getMessage(MESSAGE.gitignore_ioexception, ex));
		}
	}


	private void checkLICENSE() throws SAREFPipelineException {
		final File licenseFile = new File(repository.getDirectory(), "LICENSE");
		if (!licenseFile.exists()) {
			logError(getMessage(MESSAGE.license_missing));
			return;
		}
		InputStream licenseModelFile = Clause_9_2_Checker.class.getClassLoader()
				.getResourceAsStream("LICENSE_MODEL");
		try (BufferedReader licenseModelFileReader = new BufferedReader(new InputStreamReader(licenseModelFile));
				BufferedReader licenseFileReader = new BufferedReader(new FileReader(licenseFile));) {

			// first line
			String modelLine = licenseModelFileReader.readLine();
			String licenseLine = licenseFileReader.readLine();
			String regex = modelLine.replaceFirst("YYYY", "(?<year>\\\\d{4})");
			Pattern pattern = Pattern.compile(String.format("^%s$", regex));
			Matcher regexMatcher = pattern.matcher(licenseLine);
			if (!regexMatcher.find()) {
				logError(getMessage(MESSAGE.license_firstline, licenseLine, modelLine));
				return;
			}
			int year = Integer.parseInt(regexMatcher.group("year"));
			int currentYear = Calendar.getInstance().get(Calendar.YEAR);
			if (year < 2015 || year > currentYear) {
				logWarning(getMessage(MESSAGE.license_year, 2015, currentYear));
				return;
			}

			int i = 1;
			modelLine = licenseModelFileReader.readLine();
			licenseLine = licenseFileReader.readLine();
			while (modelLine != null && licenseLine != null) {
				i++;
				if (!modelLine.equals(licenseLine)) {
					logError(getMessage(MESSAGE.license_line, i, modelLine));
					return;
				}
				modelLine = licenseModelFileReader.readLine();
				licenseLine = licenseFileReader.readLine();
			}
			if (modelLine == null && licenseLine != null) {
				logError(getMessage(MESSAGE.license_overcomplete, i, modelLine));
				return;
			}
			if (modelLine != null && licenseLine == null) {
				logError(getMessage(MESSAGE.license_incomplete, i));
				return;
			}
		} catch (Exception ex) {
			logError(getMessage(MESSAGE.license_ioexception, ex));
		}
	}

	private void checkDirectory(String dirName, MESSAGE message) throws SAREFPipelineException {
		final File dir = new File(repository.getDirectory(), dirName);
		try {
			if (!dir.isDirectory()) {
				String msg = getMessage(message, dirName);
				logError(msg);
			}
		} catch (SecurityException ex) {
			logError(getMessage(MESSAGE.securityexception, ex));
		}
	}

}
