package org.bndtools.versioncontrol.ignores.plugin.git;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.LinkedList;
import java.util.List;

import org.bndtools.api.NamedPlugin;
import org.bndtools.versioncontrol.ignores.manager.api.VersionControlIgnoresPlugin;
import org.osgi.service.component.annotations.Component;

@Component
public class GitVersionControlIgnoresPlugin implements VersionControlIgnoresPlugin {
	private static final String GITIGNORE_FILE_NAME = ".gitignore";

	/**
	 * Fully read an ignore file, including comments.
	 *
	 * @param ignoreFile The ignore file
	 * @return null when the ignore file is null, when the ignore file doesn't
	 *         exist or when the ignore file is empty. A non-empty list of lines
	 *         as read from the ignore file otherwise.
	 * @throws IOException When the ignore file could not be fully read (for
	 *             example due to the ignore file not being an regular file or
	 *             due to an IOException)
	 */
	private List<String> readIgnoreFile(File ignoreFile) throws IOException {
		if (ignoreFile == null || !ignoreFile.exists()) {
			return null;
		}

		List<String> result = new LinkedList<>();

		int lineNr = 0;
		try (BufferedReader reader = Files.newBufferedReader(ignoreFile.toPath(), Charset.forName("UTF-8"))) {
			String line;
			while ((line = reader.readLine()) != null) {
				result.add(line);
				lineNr++;
			}
		} catch (Exception e) {
			throw new IOException(
				String.format("Error reading ignore file %s on line %d", ignoreFile.getAbsolutePath(), lineNr), e);
		}

		if (result.isEmpty()) {
			return null;
		}

		return result;
	}

	/*
	 * VersionControlIgnoresPlugin
	 */

	@Override
	public NamedPlugin getInformation() {
		return new GitVersionControlIgnoresPluginInformation();
	}

	@Override
	public boolean canStoreEmptyDirectories() {
		return false;
	}

	@Override
	public boolean matchesRepositoryProviderId(String repositoryProviderId) {
		return "org.eclipse.egit.core.GitProvider".equals(repositoryProviderId);
	}

	@Override
	public void addIgnores(File dstDir, List<String> ignores) throws Exception {
		if (dstDir == null) {
			return;
		}

		List<String> newIgnores = ignores;
		if (newIgnores == null) {
			newIgnores = new LinkedList<>();
		}

		/* create the directory of the ignore file, when needed */
		if (!dstDir.exists() && !dstDir.mkdirs()) {
			throw new IOException("Could not create directory " + dstDir.getPath());
		}

		File ignoreFile = new File(dstDir, GITIGNORE_FILE_NAME);

		List<String> ignoresToAppend;
		if (!ignoreFile.exists()) {
			ignoresToAppend = newIgnores;
		} else {
			/* read the current ignores */
			List<String> currentIgnores = readIgnoreFile(ignoreFile);

			/*
			 * add new ignores to the current ignores, but only if the current
			 * ignores did not contain them
			 */
			if (currentIgnores == null) {
				ignoresToAppend = newIgnores;
			} else {
				for (String newIgnore : newIgnores) {
					if (!currentIgnores.contains(newIgnore)) {
						currentIgnores.add(newIgnore);
					}
				}
				ignoresToAppend = currentIgnores;
			}

			/* exit when we have no new ignores to write */
			if (ignoresToAppend.isEmpty()) {
				return;
			}
		}

		/* write out the ignore file */
		try (BufferedWriter writer = Files.newBufferedWriter(ignoreFile.toPath(), Charset.forName("UTF-8"))) {
			for (String ignoreToAppend : ignoresToAppend) {
				writer.write(ignoreToAppend);
				writer.newLine();
			}
			writer.flush();
		} catch (Exception e) {
			throw new IOException(
				String.format("Error appending %s to ignore file %s", ignoresToAppend, ignoreFile.getAbsolutePath()),
				e);
		}
	}
}
