package ethanjones.cubes.core.platform;

import ethanjones.cubes.core.logging.Log;
import ethanjones.cubes.core.system.Debug;
import ethanjones.cubes.core.system.Executor;
import ethanjones.cubes.graphics.menu.Menu;
import ethanjones.cubes.graphics.menus.MainMenu;
import ethanjones.cubes.graphics.menus.RunnableMenu;
import ethanjones.cubes.input.InputChain;
import ethanjones.cubes.side.client.CubesClient;
import ethanjones.cubes.side.common.Side;
import ethanjones.cubes.side.server.CubesServer;
import ethanjones.cubes.world.thread.WorldTasks;

import com.badlogic.gdx.Gdx;

public class Adapter {

  private static final int JOIN_TIMEOUT = 60000;
  private static AdapterInterface adapter;

  public static void setClient(CubesClient cubesClient) throws UnsupportedOperationException {
    adapter.setClient(cubesClient);
  }

  public static void setServer(CubesServer cubesServer) throws UnsupportedOperationException {
    adapter.setServer(cubesServer);
  }

  public static Menu getMenu() {
    return adapter.getMenu();
  }

  public static void setMenu(Menu menu) throws UnsupportedOperationException {
    adapter.setMenu(menu);
  }

  public static void dispose() {
    Log.debug("Disposing adapter");
    final Menu menu = adapter.getMenu();
    try {
      if (menu != null) {
        menu.save();
        InputChain.hideMenu(menu);
      }
    } catch (Exception e) {
      Debug.crash(e);
    }
    stop();
  }

  private static void stop() {
    final CubesClient cubesClient = adapter.getClient();
    final CubesServer cubesServer = adapter.getServer();
    final Thread currentThread = Thread.currentThread();
  
    Gdx.app.postRunnable(new Runnable() {
      @Override
      public void run() {
        Gdx.app.exit();
      }
    });

    if (!isDedicatedServer()) {
      if (Adapter.getInterface().getThread() == currentThread) {
        stopFromClientThread(cubesClient, cubesServer);
      } else if (cubesServer != null && cubesServer.getThread() == currentThread) {
        stopFromServerThread(cubesClient, cubesServer);
      } else {
        stopFromOtherThread(cubesClient, cubesServer);
      }
      stopBackground();
      throw new StopLoopException();
    } else {
      if (Adapter.getInterface().getThread() == currentThread) {
        stopFromServerThread(cubesClient, cubesServer);
      } else {
        stopFromOtherThread(cubesClient, cubesServer);
      }
      stopBackground();
    }
  }

  public static boolean isDedicatedServer() {
    return adapter.getSide() == Side.Server;
  }

  public static AdapterInterface getInterface() {
    return adapter;
  }

  public static void setInterface(AdapterInterface adapterInterface) {
    if (adapter == null && adapterInterface != null) adapter = adapterInterface;
  }

  private static void stopFromClientThread(final CubesClient cubesClient, final CubesServer cubesServer) {
    try {
      if (cubesServer != null) {
        cubesServer.dispose();
      }
      if (cubesClient != null) {
        cubesClient.dispose();
      }
      if (cubesServer != null) {
        try {
          cubesServer.getThread().join(JOIN_TIMEOUT);
        } catch (InterruptedException e) {
        }
        if (cubesServer.getThread().isAlive()) {
          failedToStopThread(cubesServer.getThread());
        }
      }
    } catch (Exception e) {
      Debug.crash(e);
    }
  }

  private static void stopFromServerThread(final CubesClient cubesClient, final CubesServer cubesServer) {
    try {
      if (cubesClient != null) {
        cubesClient.dispose();
      }
      if (cubesServer != null) {
        cubesServer.dispose();
      }
//      if (cubesClient != null) {
//        try {
//          cubesClient.getThread().join(JOIN_TIMEOUT);
//        } catch (InterruptedException e) {
//        }
//        if (cubesClient.getThread().isAlive()) {
//          failedToStopThread(cubesClient.getThread());
//        }
//      }
    } catch (Exception e) {
      Debug.crash(e);
    }
  }

  private static void stopFromOtherThread(final CubesClient cubesClient, final CubesServer cubesServer) {
    try {
      if (cubesClient != null) {
        cubesClient.dispose();
      }
      if (cubesServer != null) {
        cubesServer.dispose();
      }
//      if (cubesClient != null) {
//        try {
//          cubesClient.getThread().join(JOIN_TIMEOUT);
//        } catch (InterruptedException e) {
//        }
//        if (cubesClient.getThread().isAlive()) {
//          failedToStopThread(cubesClient.getThread());
//        }
//      }
//      if (cubesServer != null) {
//        try {
//          cubesServer.getThread().join(JOIN_TIMEOUT);
//        } catch (InterruptedException e) {
//        }
//        if (cubesServer.getThread().isAlive()) {
//          failedToStopThread(cubesServer.getThread());
//        }
//      }
    } catch (Exception e) {
      Debug.crash(e);
    }
  }
  
  private static void stopBackground() {
    try {
      Executor.stop();
      WorldTasks.dispose();
    } catch (Exception ignored) {
    }
  }

  private static synchronized void failedToStopThread(Thread thread) {
    StackTraceElement[] stackTrace = thread.getStackTrace();
    Log.error("Failed to stop " + thread.getName() + " thread");
    for (StackTraceElement stackTraceElement : stackTrace) {
      Log.error("  " + stackTraceElement.toString());
    }
  }

  public static void gotoMainMenu() {
    gotoMenu(new MainMenu());
  }
  
  
  /**
   * Will exit if server
   */
  public static void gotoMenu(final Menu menu) {
    if (isDedicatedServer()) quit();
    if (menu == null || adapter.getMenu() instanceof RunnableMenu || menu.getClass().isInstance(adapter.getMenu())) return;
  
    adapter.setMenu(new RunnableMenu(new Runnable() {
      @Override
      public void run() {
        if (adapter.getClient() == null && adapter.getServer() == null) {
          adapter.setMenu(menu);
        }
      }
    }));
    stop();
  }

  public static void quit() {
    System.exit(0); // Force android to exit vm
    //Gdx.app.exit();
  }
}

@@ -1206,24 +1206,148 @@
  adapter%22);%0A
+    Gdx.app.postRunnable(new Runnable() %7B%0A      @Override%0A      public void run() %7B%0A        Gdx.app.exit();%0A      %7D%0A    %7D);%0A
     final Me
@@ -1371,24 +1371,24 @@
 .getMenu();%0A
-
     try %7B%0A  
@@ -1535,32 +1535,54 @@
   %7D%0A    stop();%0A
+    stopBackground();%0A
   %7D%0A%0A  private s
@@ -1775,135 +1775,8 @@
 d();
-%0A  %0A    Gdx.app.postRunnable(new Runnable() %7B%0A      @Override%0A      public void run() %7B%0A        Gdx.app.exit();%0A      %7D%0A    %7D);
 %0A%0A  
@@ -2148,32 +2148,8 @@
   %7D%0A
-      stopBackground();%0A
     
@@ -2381,32 +2381,32 @@
 besServer);%0A
+
       %7D%0A
       stopBa
@@ -2397,32 +2397,8 @@
   %7D%0A
-      stopBackground();%0A
     

Fix exiting to main menu closing Cubes