﻿package cmtest;


import java.lang.*;
import java.util.*;

import junit.framework.*;
import codice.test.*;


class RepositoryChecker {

    RepositoryChecker(String plasticWkPath, String gitWkPath, String wkserverName) {
        mPlasticWkPath = plasticWkPath;
        mGitWkPath = gitWkPath;
        mServerName = wkserverName;
    }

    public void checkFullRepository() {
        checkBranchHeads();

        checkChangesets();
    }

    private void logError(String info) {
		if(logger == null) {
			return;
		}
		logger.error(info);
	}

    private void checkChangesets() {
    
        List<Long> plasticChangesets = MetadataChecker.getListOfChangesets(mPlasticWkPath);

		logInfo("Checking changesets");
        
        for(Long plasticChangeset : plasticChangesets) {
            checkData(plasticChangeset);
            MetadataChecker.check(plasticChangeset, mPlasticWkPath, mGitWkPath);
        }
		logInfo("Changesets in sync");
    }

    private void checkBranchHeads() {
		String separator = ":";
        List<Branch> branches = MetadataChecker.getListOfBranches(mPlasticWkPath);
		logInfo("Checking branch heads");
        for(Branch branch : branches) {
			logInfo(String.format("Checking branch %s: %s", branch.getId(),
									branch.getName()));
            String attribute = GitHelper.getGitAttribute(branch.id, mPlasticWkPath);

			if(!attribute.contains(separator)) {
				throw new Exception(String.format("The branch %s does not have"
													+ " a valid attribute %s",
												 branch.getName(), attribute));
			}
            String[] fields = attribute.split(":");
            String refName = fields[0];
            long csetId = Long.parseLong(fields[1]);

            Assert.assertEquals(branch.Changeset, csetId, "The branch is out of sync");

            String foreignRev = GitHelper.getGitEquivalentChangeset(csetId, mPlasticWkPath);
            String gitSha = GitHelper.getReferenceCommit(refName, mGitWkPath);

            Assert.assertEquals(gitSha, foreignRev, "The branch is out of sync");
        }
		logInfo("Branch heads in sync");
    }

    private void checkMergeLinks() {
        List<Merge> plasticMergeLinks = MetadataChecker.getListOfMergeLinks(mPlasticWkPath);

        Dictionary<Long, List<String>> mergeLinks = new Hashtable<Long, List<String>>();
        for (Merge plasticMergeLink : plasticMergeLinks) {
            List<String> ids;
            if ((ids = mergeLinks.tryGetValue(plasticMergeLink.dstId, ids)) != null) {
                ids = new ArrayList<String>();
                mergeLinks.get(plasticMergeLink.dstId) = ids;
            }
            ids.add(GitHelper.getGitEquivalentChangeset(plasticMergeLink.srcId, mPlasticWkPath));
        }

        checkParentsFromDstMergeLink(mergeLinks);
    }

    private void checkParentsFromDstMergeLink(Dictionary<Long, List<String>> mergeLinks) {
        String sha = null;
        List<String> gitMergeLinks = null;

        for(Long dstId : mergeLinks.getKeys()) {
            sha = GitHelper.getGitEquivalentChangeset(dstId, mPlasticWkPath);
            gitMergeLinks = GitHelper.getMergeLinks(sha, mGitWkPath);
            Assert.assertEquals(mergeLinks.get(dstId), gitMergeLinks);
        }
    }

    private void checkBranches() {
        List<Branch> branches = MetadataChecker.getListOfBranches(mPlasticWkPath);

        // check branch heads
        for(Branch branch : branches) {
            String attribute = GitHelper.getGitAttribute(branch.id, mPlasticWkPath);

            String[] fields = attribute.split(":");
            String refName = fields[0];
            long csetId = Long.parseLong(fields[1]);

            Assert.assertEquals(branch.getChangeset(), csetId, "The branch is out of sync");

            String foreignRev = GitHelper.getGitEquivalentChangeset(csetId, mPlasticWkPath);
            String gitSha = GitHelper.getReferenceCommit(refName, mGitWkPath);

            Assert.assertEquals(gitSha, foreignRev, "The branch is out of sync");
        }

        // check all branches are sync
        Assert.assertEquals(branches.size(), GitHelper.getBranches(mGitWkPath).size(),
            "There are branches that are not synchronized.");
    }

    public void checkFullRepositoryFromDate(Date dateTime) {
        List<Cs> plasticChangesets = MetadataChecker.getListOfChangesetsFromDate(mPlasticWkPath, dateTime);

        for(Cs plasticChangeset : plasticChangesets) {
            checkData(plasticChangeset.id);
            MetadataChecker.check(plasticChangeset, mPlasticWkPath, mGitWkPath);
        }
    }

    private void checkData(long plasticChangeset) {
        String sha = GitHelper.getGitEquivalentChangeset(plasticChangeset, mPlasticWkPath);

        if (sha == null)
            Assert.fail("The changeset cs:{0} has not a git mapping", plasticChangeset);

        // update plastic changeset
        CmdRunner.executeCommand(
            String.format("cm update . --changeset=%ld -wks=%s", plasticChangeset, mServerName),
            mPlasticWkPath);

            
        // update git changeset
        GitHelper.switchElements(sha, mGitWkPath);
        
        // compare workspace contents
        compareWks(mPlasticWkPath, mGitWkPath);
    }

    private List getBranches() {
        String cmdres = CmdRunner.executeCommandWithStringResult(String.format(
            "cm find branches on repository '%s' --format={{name}} --nototal -wks=%s",
            "imported", mServerName), mPlasticWkPath);

        return ResultParser.getListResults(cmdres, false);
    }

    static void compareWks(String plasticwk, String gitWk) throws Exception {
        FolderComparator comparator = new FolderComparator(
            plasticwk,
            gitWk,
            new String[] { Config.PLASTIC_WK_DIR, ".git" },
            new String[] { ".private." });

        List result = comparator.compare();

        if (result != null && result.size() > 0) {
            StringBuilder builder = new StringBuilder();
            builder.append("Differences comparing Plastic and Git workspaces!!!!!!");
            for(FolderComparationResult folderDiff : result) {
                builder.append(
                    String.format("%s (%s)-> %s",
                    folderDiff.getItemName(),
                    folderDiff.getType().toString(),
                    folderDiff.getDifferenceMsg()));
            }

            throw new Exception(builder.toString());
        }
    }

    String mPlasticWkPath;
    String mGitWkPath;
    String mServerName;
}
