/*
 * Copyright 2000-2009 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 git4idea.rebase;

import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.openapi.util.Key;
import git4idea.commands.GitLineHandlerAdapter;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This listener gathers information relevant to the progress of the rebase operation
 */
public class GitRebaseLineListener extends GitLineHandlerAdapter {
  /**
   * Git rebase progress message
   */
  private static final Pattern PROGRESS = Pattern.compile("^Rebasing \\((\\d+)/(\\d+)\\)$");
  /**
   * The status
   */
  private Status myStatus;
  /**
   * The progress line
   */
  private String myProgressLine;

  /**
   * {@inheritDoc}
   */
  @Override
  public synchronized void onLineAvailable(String line, Key outputType) {
    if (outputType == ProcessOutputTypes.STDOUT) {
      if (PROGRESS.matcher(line).matches()) {
        myProgressLine = line;
        // a bit dodgy line since STDERR line could arrive before STDOUT line,
        // but in practice STDOUT comes first.
        myStatus = null;
      }
    }
    else {
      if (line.startsWith("You can amend the commit now")) {
        assert myStatus == null;
        myStatus = Status.EDIT;
      }
      else if (line.startsWith("Successfully rebased and updated")) {
        assert myStatus == null;
        myStatus = Status.FINISHED;
      }
      else if (line.startsWith("Automatic cherry-pick failed") || line.startsWith("When you have resolved this problem")) {
        assert myStatus == null || myStatus == Status.ERROR;
        myStatus = Status.CONFLICT;
      }
      else if (line.startsWith("Could not execute editor")) {
        assert myStatus == null;
        myStatus = myProgressLine == null ? Status.CANCELLED : Status.ERROR;
      }
      else if (line.startsWith("fatal") || line.startsWith("error: ") || line.startsWith("Cannot")) {
        if (myStatus != Status.CONFLICT) {
          myStatus = Status.ERROR;
        }
      }
    }
  }

  /**
   * @return the progress values as a pair
   */
  public synchronized Result getResult() {
    int total;
    int current;
    if (myProgressLine != null) {
      // the integers already matched the digits pattern, so they should parse
      final Matcher matcher = PROGRESS.matcher(myProgressLine);
      if (matcher.matches()) {
        current = Integer.parseInt(matcher.group(1));
        total = Integer.parseInt(matcher.group(2));
      }
      else {
        throw new IllegalStateException("The wrong current result line: " + myProgressLine);
      }
    }
    else {
      total = current = 0;
    }
    return new Result(myStatus == null ? Status.FINISHED : myStatus, total, current);
  }

  /**
   * The result of operation
   */
  public static final class Result {
    /**
     * The operation status
     */
    public final Status status;
    /**
     * The total number of commits
     */
    public final int total;
    /**
     * The commit number that is being currently processed
     */
    public final int current;

    /**
     * A constructor
     *
     * @param status  the status
     * @param total   the commit count
     * @param current the current commit
     */
    public Result(Status status, int total, int current) {
      this.status = status;
      this.total = total;
      this.current = current;
    }
  }

  /**
   * The current rebase status
   */
  public enum Status {
    /**
     * Rebase operation is cancelled (could not execute editor)
     */
    CANCELLED,
    /**
     * Rebase is finished
     */
    FINISHED,
    /**
     * Suspended for edit
     */
    EDIT,
    /**
     * Suspended due to the conflict
     */
    CONFLICT,
    /**
     * Suspended due to the error
     */
    ERROR
  }
}

@@ -995,53 +995,8 @@
 r %7B%0A
-  /**%0A   * Git rebase progress message%0A   */%0A
   pr
@@ -1088,98 +1088,8 @@
 %22);%0A
-  /**%0A   * The status%0A   */%0A  private Status myStatus;%0A  /**%0A   * The progress line%0A   */%0A
   pr
@@ -1122,39 +1122,8 @@
 e;%0A%0A
-  /**%0A   * %7B@inheritDoc%7D%0A   */%0A
   @O
@@ -1342,1131 +1342,20 @@
     
-  // a bit dodgy line since STDERR line could arrive before STDOUT line,%0A        // but in practice STDOUT comes first.%0A        myStatus = null;%0A      %7D%0A    %7D%0A    else %7B%0A      if (line.startsWith(%22You can amend the commit now%22)) %7B%0A        assert myStatus == null;%0A        myStatus = Status.EDIT;%0A      %7D%0A      else if (line.startsWith(%22Successfully rebased and updated%22)) %7B%0A        assert myStatus == null;%0A        myStatus = Status.FINISHED;%0A      %7D%0A      else if (line.startsWith(%22Automatic cherry-pick failed%22) %7C%7C line.startsWith(%22When you have resolved this problem%22)) %7B%0A        assert myStatus == null %7C%7C myStatus == Status.ERROR;%0A        myStatus = Status.CONFLICT;%0A      %7D%0A      else if (line.startsWith(%22Could not execute editor%22)) %7B%0A        assert myStatus == null;%0A        myStatus = myProgressLine == null ? Status.CANCELLED : Status.ERROR;%0A      %7D%0A      else if (line.startsWith(%22fatal%22) %7C%7C line.startsWith(%22error: %22) %7C%7C line.startsWith(%22Cannot%22)) %7B%0A        if (myStatus != Status.CONFLICT) %7B%0A          myStatus = Status.ERROR;%0A        %7D%0A      %7D%0A    %7D%0A  %7D%0A%0A  /**%0A   * @return the progress values as a pair%0A   */
+%7D%0A    %7D%0A  %7D%0A
 %0A  p
@@ -1938,230 +1938,66 @@
 ult(
-myStatus == null ? Status.FINISHED : myStatus, total, current);%0A  %7D%0A%0A  /**%0A   * The result of operation%0A   */%0A  public static final class Result %7B%0A    /**%0A     * The operation status%0A     */%0A    public final Status status;
+total, current);%0A  %7D%0A%0A  public static final class Result %7B
 %0A   
@@ -2186,194 +2186,22 @@
     
-/**%0A     * A constructor%0A     *%0A     * @param status  the status%0A     * @param total   the commit count%0A     * @param current the current commit%0A     */%0A    public Result(Status status, 
+public Result(
 int 
@@ -2226,36 +2226,8 @@
 ) %7B%0A
-      this.status = status;%0A
     
@@ -2291,408 +2291,7 @@
 %0A  %7D
-%0A%0A  /**%0A   * The current rebase status%0A   */%0A  public enum Status %7B%0A    /**%0A     * Rebase operation is cancelled (could not execute editor)%0A     */%0A    CANCELLED,%0A    /**%0A     * Rebase is finished%0A     */%0A    FINISHED,%0A    /**%0A     * Suspended for edit%0A     */%0A    EDIT,%0A    /**%0A     * Suspended due to the conflict%0A     */%0A    CONFLICT,%0A    /**%0A     * Suspended due to the error%0A     */%0A    ERROR%0A  %7D
 %0A%7D%0A

simplify the rebase line listener: used only for progress now