/*
 * Copyright 2000-2013 JetBrains s.r.o.
 *
 * Licensed 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 hg4idea.test.merge;

import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.merge.MergeData;
import com.intellij.openapi.vcs.merge.MergeProvider;
import com.intellij.openapi.vfs.VirtualFile;
import hg4idea.test.HgPlatformTest;
import hg4idea.test.HgTestUtil;
import org.testng.Assert;
import org.zmlx.hg4idea.HgVcs;

import java.io.IOException;

import static com.intellij.dvcs.test.Executor.*;
import static hg4idea.test.HgExecutor.hg;
import static hg4idea.test.HgExecutor.updateProject;

/**
 * @author Nadya Zabrodina
 */
public class HgMergeProviderTest extends HgPlatformTest {
  protected MergeProvider myMergeProvider;

  @Override
  protected void setUp() throws Exception {
    super.setUp();

    HgVcs vcs = HgVcs.getInstance(myProject);
    assertNotNull(vcs);
    myMergeProvider = vcs.getMergeProvider();
    assertNotNull(myMergeProvider);
  }

  public void testMerge2BranchesIfFileCreatedSeparatelyInBoth() throws VcsException {
    cd(myRepository);
    hg("branch branchA");
    hg("commit -m 'create branchA' ");
    String aFile = "A.txt";
    touch(aFile, "a");
    hg("add " + aFile);
    hg("commit -m 'create file in branchA' ");
    hg("up default");
    touch(aFile, "default");
    hg("add " + aFile);
    hg("commit -m 'create file in default branch'");
    hg("merge branchA");
    verifyMergeData(myRepository.findChild(aFile), "", "default", "a");
  }

  public void testMerge2Branches() throws VcsException {
    cd(myRepository);
    String aFile = "A.txt";
    touch(aFile, "base");
    hg("add " + aFile);
    hg("commit -m 'create file'");
    hg("branch branchA");
    hg("commit -m 'create branchA'");
    echo(aFile, " modify with a");
    hg("commit -m 'modify file in branchA'");
    hg("up default");
    echo(aFile, " modify with b");
    hg("commit -m 'modify file in default'");
    hg("merge branchA");
    verifyMergeData(myRepository.findChild(aFile), "base", "base modify with b", "base modify with a");
  }

  public void testMergeWithCommittedLocalChange() throws Exception {
    prepareSecondRepository();
    final Pair<VirtualFile, VirtualFile> files = prepareFileInBothRepositories();
    final VirtualFile parentFile = files.first;
    final VirtualFile childFile = files.second;
    //Edit the file in parent repository, commit the change.
    cd(myRepository);
    HgTestUtil.printToFile(parentFile, "server");
    hg("commit -m " + COMMIT_MESSAGE);
    //Edit the file in child repository, commit the change.
    cd(myChildRepo);
    HgTestUtil.printToFile(childFile, "local");
    hg("commit -m " + COMMIT_MESSAGE);
    updateProject();
    //committing conflicting change
    verifyMergeData(myChildRepo.findChild(childFile.getName()), "basic", "local", "server");
  }

  public void testMergeWithUncommittedLocalChange() throws Exception {
    prepareSecondRepository();
    final Pair<VirtualFile, VirtualFile> files = prepareFileInBothRepositories();
    final VirtualFile parentFile = files.first;
    final VirtualFile childFile = files.second;
    //Edit the file in parent repository, commit the change.
    cd(myRepository);
    HgTestUtil.printToFile(parentFile, "server");
    hg("commit -m " + COMMIT_MESSAGE);

    // Edit the file in child repository, don't commit the change.
    cd(myChildRepo);
    HgTestUtil.printToFile(childFile, "local");
    updateProject();
    //uncommitted conflicting change
    verifyMergeData(myChildRepo.findChild(childFile.getName()), "basic", "local", "server");
  }

  public void testFileAddedAndCommitted() throws Exception {
    // this is needed to have the same root changeset - otherwise conflicting root changeset will cause
    // an error during 'hg pull': "abort: repository is unrelated"
    prepareSecondRepository();
    prepareFileInBothRepositories();
    //Add a file in parent repository, commit.
    cd(myRepository);
    String bFile = "B.txt";
    touch(bFile, "server");
    hg("add " + bFile);
    hg("commit -m " + COMMIT_MESSAGE);
    //Add a file with the same name, but different content in child repository, commit.
    cd(myChildRepo);
    touch(bFile, "local");
    hg("add " + bFile);
    hg("commit -m " + COMMIT_MESSAGE);

    updateProject();

    verifyMergeData(myChildRepo.findChild(bFile), "", "local", "server");
  }

  public void testFileAddedNotCommitted() throws Exception {
    // this is needed to have the same root changeset - otherwise conflicting root changeset will cause
    // an error during 'hg pull': "abort: repository is unrelated"
    prepareSecondRepository();
    prepareFileInBothRepositories();
    // Add a file in parent repository, commit.
    cd(myRepository);
    String bFile = "B.txt";
    touch(bFile, "server");
    hg("add " + bFile);
    hg("commit -m " + COMMIT_MESSAGE);
    //Add a file with the same name, but different content in child repository, don't commit.
    cd(myChildRepo);
    touch(bFile, "local");
    hg("add " + bFile);

    updateProject();

    verifyMergeData(myChildRepo.findChild(bFile), "", "local", "server");
  }


  /**
   * Creates a file with initial content in the parent repository, pulls & updates it to the child repository.
   *
   * @return References to the files in parent and child repositories respectively.
   */
  private Pair<VirtualFile, VirtualFile> prepareFileInBothRepositories() throws IOException {
    cd(myRepository);
    String aFile = "A.txt";
    touch(aFile, "basic");
    hg("add " + aFile);
    hg("commit -m 'create file' ");
    hg("update");
    myRepository.refresh(false, true);
    final VirtualFile parentFile = myRepository.findChild(aFile);
    assertNotNull("Can't find " + aFile + " in parent repo!", parentFile);
    cd(myChildRepo);
    hg("pull");
    hg("update");
    myChildRepo.refresh(false, true);
    final VirtualFile childFile = myChildRepo.findChild(aFile);
    return Pair.create(parentFile, childFile);
  }

  private void verifyMergeData(final VirtualFile file, String expectedBase, String expectedLocal, String expectedServer)
    throws VcsException {
    final MergeData mergeData = myMergeProvider.loadRevisions(file);
    assertEquals(expectedBase, mergeData.ORIGINAL);
    assertEquals(expectedServer, mergeData.LAST);
    assertEquals(expectedLocal, mergeData.CURRENT);
  }

  private static void assertEquals(String s, byte[] bytes) {
    Assert.assertEquals(s, new String(bytes));
  }
}

@@ -5554,32 +5554,70 @@
 File, %22local%22);%0A
+    myChildRepo.refresh(false, true);%0A
     hg(%22add %22 + 
@@ -5638,33 +5638,32 @@
 pdateProject();%0A
-%0A
     verifyMergeD

refresh added to merge provider test