/**
 * rscplus
 *
 * <p>This file is part of rscplus.
 *
 * <p>rscplus is free software: you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * <p>rscplus is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * <p>You should have received a copy of the GNU General Public License along with rscplus. If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * <p>Authors: see <https://github.com/RSCPlus/rscplus>
 */
package Game;

import Client.JConfig;
import Client.Launcher;
import Client.Logger;
import Client.NotificationsHandler;
import Client.Settings;
import Client.TrayHandler;
import Client.Util;
import java.applet.Applet;
import java.applet.AppletContext;
import java.applet.AppletStub;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.*;
import java.net.URL;
import java.text.DecimalFormat;
import javax.swing.JFrame;

/** Singleton class that handles packaging the client into a JFrame and starting the applet. */
public class Game extends JFrame
    implements AppletStub, ComponentListener, WindowListener, FocusListener {

  // Singleton
  private static Game instance = null;

  private JConfig m_config = new JConfig();
  private Applet m_applet = null;
  private String m_title = "";

  private Game() {
    // Empty private constructor to prevent extra instances from being created.
  }

  public void setApplet(Applet applet) {
    m_applet = applet;
    m_applet.setStub(this);
  }

  public Applet getApplet() {
    return m_applet;
  }

  /** Builds the main game client window and adds the applet to it. */
  public void start() {
    if (m_applet == null) return;

    // Set window icon
    setIconImage(Launcher.icon.getImage());

    // Set window properties
    setResizable(true);
    addWindowListener(this);
    setMinimumSize(new Dimension(1, 1));

    // Add applet to window
    setContentPane(m_applet);
    getContentPane().setBackground(Color.BLACK);
    getContentPane().setPreferredSize(new Dimension(512, 346));
    addComponentListener(this);
    pack();

    // Hide cursor if software cursor
    Settings.checkSoftwareCursor();

    // Position window and make it visible
    setLocationRelativeTo(null);
    setVisible(true);

    updateTitle();

    Reflection.Load();
    Renderer.init();

    if (!Util.isMacOS() && Settings.CUSTOM_CLIENT_SIZE.get(Settings.currentProfile)) {
      Game.getInstance().resizeFrameWithContents();
    }

    setDropTarget(ReplayQueue.dropReplays);
  }

  public JConfig getJConfig() {
    return m_config;
  }

  /** Starts the game applet. */
  public void launchGame() {
    m_config.changeWorld(Settings.WORLD.get(Settings.currentProfile));
    m_applet.addFocusListener(this);
    m_applet.init();
    m_applet.start();
  }

  public void updateTitle() {
    String title = "rscplus (";

    if (!Replay.isPlaying) {
      title += Settings.WORLD_NAMES.get(Settings.WORLD.get(Settings.currentProfile));

      if (Client.player_name.length() != 0) {
        title += "; " + Client.player_name;
      }
    } else {
      String elapsed = Util.formatTimeDuration(Replay.elapsedTimeMillis(), Replay.endTimeMillis());
      String end = Util.formatTimeDuration(Replay.endTimeMillis(), Replay.endTimeMillis());
      title += elapsed + " / " + end;
      title += ", Speed: " + new DecimalFormat("##.##").format(Replay.fpsPlayMultiplier) + "x";
      if (Replay.paused) title += ", Paused";
    }

    if (Replay.isRecording) {
      String elapsed =
          Util.formatTimeDuration(Replay.elapsedTimeMillis(), Replay.elapsedTimeMillis());
      title += "; Recording: " + elapsed;
    }

    title += ")";

    if (m_title.equals(title)) {
      return;
    }
    m_title = title;
    super.setTitle(m_title);
  }

  /*
   * FocusListener methods
   */

  @Override
  public final void focusGained(FocusEvent e) {}

  @Override
  public final void focusLost(FocusEvent e) {
    KeyboardHandler.keyUp = false;
    KeyboardHandler.keyDown = false;
    KeyboardHandler.keyLeft = false;
    KeyboardHandler.keyRight = false;
    KeyboardHandler.keyShift = false;
  }

  /*
   * AppletStub methods
   */

  @Override
  public final URL getCodeBase() {
    return m_config.getURL("codebase");
  }

  @Override
  public final URL getDocumentBase() {
    return getCodeBase();
  }

  @Override
  public final String getParameter(String key) {
    return m_config.parameters.get(key);
  }

  @Override
  public final AppletContext getAppletContext() {
    return null;
  }

  @Override
  public final void appletResize(int width, int height) {}

  /*
   * WindowListener methods
   */

  @Override
  public final void windowClosed(WindowEvent e) {
    if (m_applet == null) return;

    m_applet.stop();
    m_applet.destroy();

    Logger.stop();
  }

  @Override
  public final void windowClosing(WindowEvent e) {
    dispose();
    Launcher.getConfigWindow().disposeJFrame();
    Launcher.getQueueWindow().disposeJFrame();
    TrayHandler.removeTrayIcon();
    NotificationsHandler.closeNotificationSoundClip();
    NotificationsHandler.disposeNotificationHandler();
  }

  @Override
  public final void windowOpened(WindowEvent e) {}

  @Override
  public final void windowDeactivated(WindowEvent e) {}

  @Override
  public final void windowActivated(WindowEvent e) {}

  @Override
  public final void windowDeiconified(WindowEvent e) {}

  @Override
  public final void windowIconified(WindowEvent e) {}

  /*
   * ComponentListener methods
   */

  @Override
  public final void componentHidden(ComponentEvent e) {}

  @Override
  public final void componentMoved(ComponentEvent e) {}

  @Override
  public final void componentResized(ComponentEvent e) {
    if (m_applet == null) return;

    // Handle minimum size and launch game
    // TODO: This is probably a bad spot and should be moved
    if (getMinimumSize().width == 1) {
      setMinimumSize(getSize());
      launchGame();

      // This workaround appears to be for a bug in the macOS JVM
      // Without it, mac users get very angry
      if (Util.isMacOS()) {
        setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH);
        setLocationRelativeTo(null);
      }
    }

    Renderer.resize(getContentPane().getWidth(), getContentPane().getHeight());
  }

  @Override
  public final void componentShown(ComponentEvent e) {}

  /**
   * Gets the game client instance. It makes one if one doesn't exist.
   *
   * @return The game client instance
   */
  public static Game getInstance() {
    if (instance == null) {
      synchronized (Game.class) {
        instance = new Game();
      }
    }
    return instance;
  }

  /**
   * Resizes the Game window to match the X and Y values stored in Settings. The applet's size will
   * be recalculated on the next rendering tick.
   */
  public void resizeFrameWithContents() {
    int windowWidth =
        Settings.CUSTOM_CLIENT_SIZE_X.get(Settings.currentProfile)
            + getInsets().left
            + getInsets().right;
    int windowHeight =
        Settings.CUSTOM_CLIENT_SIZE_Y.get(Settings.currentProfile)
            + getInsets().top
            + getInsets().bottom;
    setSize(windowWidth, windowHeight);
    setLocationRelativeTo(null);
  }
}