package jetbrains.mps.execution.configurations.implementation.plugin.plugin;

/*Generated by MPS */

import com.intellij.execution.process.BaseOSProcessHandler;
import jetbrains.mps.logging.Logger;
import java.util.concurrent.Future;
import com.intellij.execution.process.ProcessOutputTypes;
import org.jetbrains.annotations.NotNull;
import com.intellij.util.io.BaseOutputReader;
import com.intellij.util.io.BaseDataReader;
import java.io.IOException;
import java.io.IOError;
import org.jetbrains.annotations.Nullable;
import java.io.OutputStream;
import com.intellij.openapi.util.Key;
import java.io.Reader;

public abstract class FakeProcessHandler extends BaseOSProcessHandler {
  private static final Logger LOG = Logger.getLogger(FakeProcessHandler.class);
  private final FakeProcess myFakeProcess;
  private final Future<?> myFuture;

  private final BlockingReader myOutputReader = new BlockingReader(createProcessOutReader(), ProcessOutputTypes.STDOUT, "output stream of " + myPresentableName);
  private final BlockingReader myErrorReader = new BlockingReader(createProcessErrReader(), ProcessOutputTypes.STDERR, "error stream of " + myPresentableName);

  public FakeProcessHandler(@NotNull FakeProcess fakeProcess, Future<?> future) {
    super(fakeProcess, "Simulating the start of a new process", null);
    myFakeProcess = fakeProcess;
    myFuture = future;
  }

  @NotNull
  @Override
  protected final BaseOutputReader.Options readerOptions() {
    return BaseOutputReader.Options.BLOCKING;
  }

  @NotNull
  @Override
  protected final BaseDataReader createErrorDataReader() {
    return myErrorReader;
  }

  @NotNull
  @Override
  protected final BaseDataReader createOutputDataReader() {
    return myOutputReader;
  }

  @Override
  public void startNotify() {
    super.startNotify();
    try {
      myFakeProcess.init();
    } catch (IOException e) {
      if (LOG.isErrorLevel()) {
        LOG.error("Process could not be constructed", e);
      }
      throw new IOError(e);
    }
    myOutputReader.startReading();
    myErrorReader.startReading();
  }

  protected void requestTerminate() {
    // no-op
  }

  @Override
  protected void destroyProcessImpl() {
    requestTerminate();
  }

  @Override
  protected void detachProcessImpl() {
    requestTerminate();
  }

  @Override
  public boolean isProcessTerminated() {
    return myFuture.isDone();
  }

  @Override
  public boolean detachIsDefault() {
    return false;
  }

  @Nullable
  @Override
  public OutputStream getProcessInput() {
    return null;
  }

  private class BlockingReader extends BaseOutputReader {
    private final Key myProcessOutputType;
    private final String myPresentableName;

    public BlockingReader(Reader reader, Key outputType, @NotNull String presentableName) {
      super(reader, BaseOutputReader.Options.BLOCKING);
      myProcessOutputType = outputType;
      myPresentableName = presentableName;
    }

    public void startReading() {
      start(myPresentableName);
    }

    @Override
    protected void doRun() {
      try {
        boolean stopSignalled = false;
        while (true) {
          final boolean read = readAvailableBlocking();
          if (stopSignalled) {
            break;
          }
          stopSignalled = isStopped;
          if (!(stopSignalled)) {
            synchronized (mySleepMonitor) {
              mySleepMonitor.wait(mySleepingPolicy.getTimeToSleep(read));
            }
          }
        }
      } catch (IOException e) {
        if (LOG.isInfoLevel()) {
          LOG.info("", e);
        }
      } catch (Exception e) {
        if (LOG.isErrorLevel()) {
          LOG.error("", e);
        }
      } finally {
        try {
          close();
        } catch (IOException e) {
          if (LOG.isErrorLevel()) {
            LOG.error("Cannot close stream", e);
          }
        }
      }
    }

    @NotNull
    @Override
    protected Future<?> executeOnPooledThread(@NotNull Runnable runnable) {
      return FakeProcessHandler.this.executeTask(runnable);
    }

    @Override
    protected void onTextAvailable(@NotNull String text) {
      notifyTextAvailable(text, myProcessOutputType);
    }
  }
}
