package org.jetbrains.ide;

import com.intellij.notification.Notification;
import com.intellij.notification.NotificationDisplayType;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.ShutDownTracker;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.io.BuiltInServer;

import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;

public class BuiltInServerManagerImpl extends BuiltInServerManager {
  private static final Logger LOG = Logger.getInstance(BuiltInServerManager.class);

  @NonNls
  public static final String PROPERTY_RPC_PORT = "rpc.port";
  private static final int FIRST_PORT_NUMBER = 63342;
  private static final int PORTS_COUNT = 20;

  private volatile int detectedPortNumber = -1;
  private final AtomicBoolean started = new AtomicBoolean(false);

  @Nullable
  private BuiltInServer server;
  private boolean enabledInUnitTestMode = true;

  static {
    // IDEA-120811
    System.setProperty("io.netty.machineId", Integer.toHexString(new Random().nextInt()));
    System.setProperty("io.netty.processId", "1");
  }

  @Override
  public int getPort() {
    return detectedPortNumber == -1 ? getDefaultPort() : detectedPortNumber;
  }

  @Override
  public BuiltInServerManager waitForStart() {
    Future<?> serverStartFuture = startServerInPooledThread();
    if (serverStartFuture != null) {
      LOG.assertTrue(ApplicationManager.getApplication().isUnitTestMode() || !ApplicationManager.getApplication().isDispatchThread());
      try {
        serverStartFuture.get();
      }
      catch (InterruptedException ignored) {
      }
      catch (ExecutionException ignored) {
      }
    }
    return this;
  }

  private static int getDefaultPort() {
    return System.getProperty(PROPERTY_RPC_PORT) == null ? FIRST_PORT_NUMBER : Integer.parseInt(System.getProperty(PROPERTY_RPC_PORT));
  }

  static final class MyPostStartupActivity implements StartupActivity, DumbAware {
    private boolean veryFirstProjectOpening = true;

    @Override
    public void runActivity(@NotNull Project project) {
      if (!veryFirstProjectOpening) {
        return;
      }

      veryFirstProjectOpening = false;
      BuiltInServerManager builtInServerManager = BuiltInServerManager.getInstance();
      if (builtInServerManager instanceof BuiltInServerManagerImpl) {
        ((BuiltInServerManagerImpl)builtInServerManager).startServerInPooledThread();
      }
    }
  }

  private Future<?> startServerInPooledThread() {
    Application application = ApplicationManager.getApplication();
    if (application.isUnitTestMode() && !enabledInUnitTestMode) {
      return null;
    }

    if (!started.compareAndSet(false, true)) {
      return null;
    }

    return application.executeOnPooledThread(new Runnable() {
      @Override
      public void run() {
        int defaultPort = getDefaultPort();
        int workerCount = 1;
        // if user set special port number for some service (eg built-in web server), we should slightly increase worker count
        if (Runtime.getRuntime().availableProcessors() > 1) {
          for (CustomPortServerManager customPortServerManager : CustomPortServerManager.EP_NAME.getExtensions()) {
            if (customPortServerManager.getPort() != defaultPort) {
              workerCount = 2;
              break;
            }
          }
        }

        try {
          server = new BuiltInServer();
          detectedPortNumber = server.start(workerCount, defaultPort, PORTS_COUNT, true);
        }
        catch (Exception e) {
          LOG.info(e);
          String groupDisplayId = "Built-in Server";
          Notifications.Bus.register(groupDisplayId, NotificationDisplayType.STICKY_BALLOON);
          new Notification(groupDisplayId, "Internal HTTP server disabled",
                           "Cannot start internal HTTP server. Git integration, JavaScript debugger and LiveEdit may operate with errors. " +
                           "Please check your firewall settings and restart " + ApplicationNamesInfo.getInstance().getFullProductName(),
                           NotificationType.ERROR).notify(null);
          return;
        }

        if (detectedPortNumber == -1) {
          LOG.info("built-in server cannot be started, cannot bind to port");
          return;
        }

        LOG.info("built-in server started, port " + detectedPortNumber);

        Disposer.register(ApplicationManager.getApplication(), server);
        ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
          @Override
          public void run() {
            if (!Disposer.isDisposed(server)) {
              // something went wrong
              Disposer.dispose(server);
            }
          }
        });
      }
    });
  }

  @Override
  @Nullable
  public Disposable getServerDisposable() {
    return server;
  }

  /**
   * Pass true to start the WebServerManager even in the {@link Application#isUnitTestMode() unit test mode}.
   */
  @TestOnly
  public void setEnabledInUnitTestMode(boolean enabled) {
    enabledInUnitTestMode = enabled;
  }
}
@@ -938,33 +938,8 @@
 r;%0A%0A
-import java.util.Random;%0A
 impo
@@ -1600,24 +1600,26 @@
  = true;%0A%0A  
+//
 static %7B%0A   
@@ -1617,16 +1617,18 @@
 tic %7B%0A  
+//
   // IDE
@@ -1638,16 +1638,18 @@
 20811%0A  
+//
   System
@@ -1731,16 +1731,18 @@
 ()));%0A  
+//
   System
@@ -1780,24 +1780,26 @@
 d%22, %221%22);%0A  
+//
 %7D%0A%0A  @Overri

revert IDEA-120811