/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.commonwl.view.git;

import org.apache.commons.codec.digest.DigestUtils;
import org.commonwl.view.researchobject.HashableAgent;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.revwalk.RevCommit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.apache.jena.ext.com.google.common.io.Files.createTempDir;

/**
 * Handles Git related functionality
 */
@Service
public class GitService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // Location to check out git repositories into
    private Path gitStorage;

    // Whether submodules are also cloned
    private boolean cloneSubmodules;

    @Autowired
    public GitService(@Value("${gitStorage}") Path gitStorage,
                      @Value("${gitAPI.cloneSubmodules}") boolean cloneSubmodules) {
        this.gitStorage = gitStorage;
        this.cloneSubmodules = cloneSubmodules;
    }

    /**
     * Gets a repository, cloning into a local directory or
     * @param gitDetails The details of the Git repository
     * @param reuseDir Whether the cached repository can be used
     * @returns The git object for the repository
     */
    public Git getRepository(GitDetails gitDetails, boolean reuseDir)
            throws GitAPIException {
        Git repo = null;
        try {
            if (reuseDir) {
                // Base dir from configuration, name from hash of repository URL
                File baseDir = new File(gitStorage.toString());
                String baseName = DigestUtils.shaHex(GitDetails.normaliseUrl(gitDetails.getRepoUrl()));

                // Check if folder already exists
                File repoDir = new File(baseDir, baseName);
                if (repoDir.exists() && repoDir.isDirectory()) {
                    repo = Git.open(repoDir);
                    repo.fetch().call();
                } else {
                    // Create a folder and clone repository into it
                    if (repoDir.mkdir()) {
                        repo = Git.cloneRepository()
                                .setCloneSubmodules(cloneSubmodules)
                                .setURI(gitDetails.getRepoUrl())
                                .setDirectory(repoDir)
                                .setCloneAllBranches(true)
                                .call();
                    }
                }
            } else {
                // Another thread is already using the existing folder
                // Must create another temporary one
                repo = Git.cloneRepository()
                        .setCloneSubmodules(cloneSubmodules)
                        .setURI(gitDetails.getRepoUrl())
                        .setDirectory(createTempDir())
                        .setCloneAllBranches(true)
                        .call();
            }

            // Checkout the specific branch or commit ID
            if (repo != null) {
                System.out.println("Listing local branches:");
                    List<Ref> call = repo.branchList().call();
                    for (Ref ref : call) {
                        System.out.println("Branch: " + ref + " " + ref.getName() + " " + ref.getObjectId().getName());
                    }

                    System.out.println("Now including remote branches:");
                    call = repo.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();
                    for (Ref ref : call) {
                        System.out.println("Branch: " + ref + " " + ref.getName() + " " + ref.getObjectId().getName());
                    }

                // Create a new local branch if it does not exist and not a commit ID
                boolean createBranch = !ObjectId.isId(gitDetails.getBranch());
                if (createBranch) {
                    Ref ref = repo.getRepository().exactRef("refs/heads/" + gitDetails.getBranch());
                    if (ref != null) {
                        createBranch = false;
                    }
                }
                repo.checkout()
                        .setCreateBranch(createBranch)
                        .setName(gitDetails.getBranch())
                        .call();
                String branch = repo.getRepository().getFullBranch();
                if (branch != null && !branch.startsWith(gitDetails.getBranch())) {
                    repo.pull().call();
                }
            }
        } catch (IOException ex) {
            logger.error("Could not open existing Git repository for '"
                    + gitDetails.getRepoUrl() + "'", ex);
        }

        return repo;
    }

    /**
     * Gets the commit ID of the HEAD for the given repository
     * @param repo The Git repository
     * @return The commit ID of the HEAD for the repository
     * @throws IOException If the HEAD is detached
     */
    public String getCurrentCommitID(Git repo) throws IOException {
        return repo.getRepository().findRef("HEAD").getObjectId().getName();
    }

    /**
     * Gets a set of authors for a path in a given repository
     * @param repo The git repository
     * @param path The path to get commits for
     * @return An iterable of commits
     * @throws GitAPIException Any API errors which may occur
     * @throws URISyntaxException Error constructing mailto link
     */
    public Set<HashableAgent> getAuthors(Git repo, String path) throws GitAPIException, URISyntaxException {
        Iterable<RevCommit> logs = repo.log().addPath(path).call();
        Set<HashableAgent> fileAuthors = new HashSet<>();
        for (RevCommit rev : logs) {
            // Use author first with backup of committer
            PersonIdent author = rev.getAuthorIdent();
            if (author == null) {
                author = rev.getCommitterIdent();
            }
            // Create a new agent and add as much detail as possible
            if (author != null) {
                HashableAgent newAgent = new HashableAgent();
                String name = author.getName();
                if (name != null && name.length() > 0) {
                    newAgent.setName(author.getName());
                }
                String email = author.getEmailAddress();
                if (email != null && email.length() > 0) {
                    newAgent.setUri(new URI("mailto:" + author.getEmailAddress()));
                }
                fileAuthors.add(newAgent);
            }
        }
        return fileAuthors;
    }

}

@@ -978,55 +978,8 @@
 it;%0A
-import org.eclipse.jgit.api.ListBranchCommand;%0A
 impo
@@ -1571,31 +1571,8 @@
 et;%0A
-import java.util.List;%0A
 impo
@@ -4251,677 +4251,8 @@
 ) %7B%0A
-                System.out.println(%22Listing local branches:%22);%0A                    List%3CRef%3E call = repo.branchList().call();%0A                    for (Ref ref : call) %7B%0A                        System.out.println(%22Branch: %22 + ref + %22 %22 + ref.getName() + %22 %22 + ref.getObjectId().getName());%0A                    %7D%0A%0A                    System.out.println(%22Now including remote branches:%22);%0A                    call = repo.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();%0A                    for (Ref ref : call) %7B%0A                        System.out.println(%22Branch: %22 + ref + %22 %22 + ref.getName() + %22 %22 + ref.getObjectId().getName());%0A                    %7D%0A%0A
     

Remove debug listing of branches