/*
 * 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 com.intellij.execution.testframework.ui;

import com.intellij.execution.filters.HyperlinkInfo;
import com.intellij.execution.testframework.*;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.openapi.Disposable;
import org.jetbrains.annotations.NotNull;

public class TestsOutputConsolePrinter implements Printer, Disposable {
  private final ConsoleView myConsole;
  private final TestConsoleProperties myProperties;
  private final AbstractTestProxy myUnboundOutputRoot;
  private AbstractTestProxy myCurrentTest;

  // After pause action has been invoked -  all output will be redirected to special
  // myPausedPrinter which will dump all buffered data after user will continue process.
  private final DeferingPrinter myPausedPrinter = new DeferingPrinter();
  private boolean myPaused = false;

  private int myMarkOffset = 0;

  private final TestFrameworkPropertyListener<Boolean> myPropertyListener = new TestFrameworkPropertyListener<Boolean>() {
        public void onChanged(final Boolean value) {
          if (!value.booleanValue()) myMarkOffset = 0;
        }
      };

  public TestsOutputConsolePrinter(final ConsoleView console, final TestConsoleProperties properties, final AbstractTestProxy unboundOutputRoot) {
    myConsole = console;
    myProperties = properties;
    myUnboundOutputRoot = unboundOutputRoot;
    myProperties.addListener(TestConsoleProperties.SCROLL_TO_STACK_TRACE, myPropertyListener);
  }

  public ConsoleView getConsole() {
    return myConsole;
  }

  public boolean isPaused() {
    return myPaused;
  }

  public void pause(final boolean doPause) {
    myPaused = doPause;
    if (!doPause) {
      myPausedPrinter.printAndForget(this);
    }
  }

  public void print(final String text, final ConsoleViewContentType contentType) {
    myConsole.print(text, contentType);
  }

  public void onNewAvailable(@NotNull final Printable printable) {
    if (myPaused) {
      printable.printOn(myPausedPrinter);
    } else {
      printable.printOn(this);
    }
  }

  /**
   * Clears console, prints output of selected test and scrolls to beginning
   * of output.
   * This method must be invoked in Event Dispatch Thread
   * @param test Selected test
   */
  public void updateOnTestSelected(final AbstractTestProxy test) {
    if (myCurrentTest == test) {
      return;
    }
    if (myCurrentTest != null) {
      myCurrentTest.setPrinter(null);
    }
    myMarkOffset = 0;
    if (test == null) {
      myCurrentTest = null;
      return;
    }
    myCurrentTest = test;
    myCurrentTest.setPrinter(this);
    final Runnable clearRunnable = new Runnable() {
      public void run() {
        myConsole.clear();
      }
    };
    final Runnable scrollRunnable = new Runnable() {
      @Override
      public void run() {
        scrollToBeginning();
      }
    };
    if (isRoot() && myUnboundOutputRoot != null) {
      myUnboundOutputRoot.invokeInAlarm(clearRunnable);
      myUnboundOutputRoot.printOn(this);
      myUnboundOutputRoot.invokeInAlarm(scrollRunnable);
    } else {
      myCurrentTest.invokeInAlarm(clearRunnable);
      myCurrentTest.printOn(this);
      myCurrentTest.invokeInAlarm(scrollRunnable);
    }
  }

  public boolean isCurrent(CompositePrintable printable) {
    return myCurrentTest == printable || isRoot();
  }

  private boolean isRoot() {
    return myCurrentTest != null && myCurrentTest.getParent() == myUnboundOutputRoot;
  }

  public void printHyperlink(final String text, final HyperlinkInfo info) {
    myConsole.printHyperlink(text, info);
  }

  public void mark() {
    if (TestConsoleProperties.SCROLL_TO_STACK_TRACE.value(myProperties))
      myMarkOffset = myConsole.getContentSize();
  }

  public void dispose() {
    myProperties.removeListener(TestConsoleProperties.SCROLL_TO_STACK_TRACE, myPropertyListener);
  }

  public boolean canPause() {
    return myCurrentTest != null ? myCurrentTest.isInProgress() : false;
  }

  protected void scrollToBeginning() {
    myConsole.performWhenNoDeferredOutput(new Runnable() {
      public void run() {
        myConsole.scrollTo(myMarkOffset);
      }
    });
  }
}

@@ -3497,76 +3497,96 @@
     
-if (isRoot() && myUnboundOutputRoot != null) %7B%0A      myUnboundOutput
+final AbstractTestProxy currentProxyOrRoot = getCurrentProxyOrRoot();%0A    currentProxyOr
 Root
@@ -3620,33 +3620,30 @@
 e);%0A    
-  myUnboundOutput
+currentProxyOr
 Root.pri
@@ -3658,33 +3658,30 @@
 s);%0A    
-  myUnboundOutput
+currentProxyOr
 Root.inv
@@ -3714,160 +3714,154 @@
 ;%0A  
-  %7D else %7B%0A      myCurrentTest.invokeInAlarm(clearRunnable);%0A      myCurrentTest.printOn(this);%0A      myCurrentTest.invokeInAlarm(scrollRunnable);%0A    %7D
+%7D%0A%0A  private AbstractTestProxy getCurrentProxyOrRoot() %7B%0A    return isRoot() && myUnboundOutputRoot != null ? myUnboundOutputRoot : myCurrentTest;
 %0A  %7D
@@ -4723,32 +4723,211 @@
 ic void run() %7B%0A
+        final AbstractTestProxy currentProxyOrRoot = getCurrentProxyOrRoot();%0A        if (!currentProxyOrRoot.isInProgress()) %7B%0A          //do not scroll to any mark during run%0A  
         myConsol
@@ -4952,16 +4952,26 @@
 ffset);%0A
+        %7D%0A
       %7D%0A

test console: follow running tests, scroll to the beginning for the completed tests only (IDEA-75565)