package de.renew.navigator.vc.git;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.apache.log4j.Logger;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.storage.file.FileRepository;

import de.renew.navigator.vc.AbstractRepository;
import de.renew.navigator.vc.Commit;
import de.renew.util.StringUtil;


/**
 * @author Konstantin Simon Maria Möllers
 * @version 0.1
 */
public class GitRepository extends AbstractRepository {

    /**
     * Log4j logger instance.
     */
    public static final Logger LOGGER = Logger.getLogger(GitRepository.class);

    protected final Git _git;
    protected final FileRepository _fileRepository;

    public GitRepository(FileRepository fileRepository, GitVersionControl versionControl) {
        super(fileRepository.getWorkTree(), versionControl);
        this._fileRepository = fileRepository;
        _git = new Git(fileRepository);
        setLastCommit(retrieveLastCommit());
        update();
    }

    @Override
    public String getBranch() {
        try {
            return _fileRepository.getBranch();
        } catch (IOException e) {
            return null;
        }
    }

    @Override
    public String getRemoteURL() {
        final FileBasedConfig config = _fileRepository.getConfig();
        final Set<String> remotes = config.getSubsections("remote");
        final Set<String> svnRemotes = config.getSubsections("svn-remote");

        // Create a new Set of URLs based on Remotes / SVN Remotes.
        final HashSet<String> urls = new HashSet<>(remotes.size() + svnRemotes.size());
        for (String remote : remotes) {
            urls.add(config.getString("remote", remote, "url"));
        }
        for (String svnRemote : svnRemotes) {
            urls.add(config.getString("svn-remote", svnRemote, "url"));
        }

        return StringUtil.join(urls, ",");
    }

    @Override
    public void update() {
        clearFileSets();

        try {
            final Status status = _git.status().call();
            addAllToModified(makeFileSet(status.getModified(), true));
            addAllToAdded(makeFileSet(status.getAdded(), false));
            addAllToIgnored(makeFileSet(status.getUntracked(), false));
        } catch (GitAPIException ignored) {
            // FIXME: Explain why this is ignored.
        }
    }

    /**
     * Tries to retrieve the last commit on this repository.
     */
    @Override
    protected Commit retrieveLastCommit() {
        try {
            final Iterator<RevCommit> iterator = _git.log().call().iterator();
            if (iterator.hasNext()) {
                RevCommit revCommit = iterator.next();

                // Create common commit from Git commit.
                final Commit commit = new Commit();
                commit.setAuthor(revCommit.getAuthorIdent().getName());
                commit.setDate(revCommit.getCommitTime());
                commit.setMessage(revCommit.getFullMessage());
                commit.setRevision(revCommit.getName());

                return commit;
            }
        } catch (GitAPIException e) {
            LOGGER.error("Could not get last commit", e);
        }

        return null;
    }


    /**
     * Converts a relative string file set to a true set of files.
     */
    private Set<File> makeFileSet(Set<String> gitFileSet, boolean recursive) {
        HashSet<File> target = new HashSet<>();
        String workTree = getRootDirectory().getAbsolutePath();
        File absoluteRootDir = new File(workTree);
        for (String file : gitFileSet) {
            final String filename = workTree + File.separatorChar + file;
            File newFile = new File(filename);
            if (newFile.exists()) {
                do {
                    target.add(newFile);
                    newFile = newFile.getParentFile();
                } while (recursive && !newFile.equals(absoluteRootDir));
            }
        }

        return target;
    }
}