package net.liquidpineapple.pang;

import net.liquidpineapple.pang.gui.Board;
import net.liquidpineapple.pang.gui.LifeSystem;
import net.liquidpineapple.pang.gui.ScoreSystem;
import net.liquidpineapple.pang.logger.Logger;
import net.liquidpineapple.pang.logger.LoggerTypes;
import net.liquidpineapple.pang.runnables.DrawRunnable;
import net.liquidpineapple.pang.runnables.UpdateRunnable;

import java.awt.EventQueue;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.HashMap;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.WindowConstants;

import lombok.Getter;
import lombok.Setter;

/**
 * The main application, extends {@link JFrame}.
 *
 * @author Jurriaan Den Toonder
 * @date 2016/09/06.
 */
public class Application extends JFrame {

  @Getter
  private static Application app;

  // Set to true to prevent loosing lives
  public static final boolean cheatMode = false;

  public static boolean multiplayer;
  public static HashMap<String, Image> imageCache = new HashMap<>();
  public static LifeSystem lifeSystem;

  @Getter
  private static Board board;

  @Getter
  @Setter
  private static ScoreSystem scoreSystem;

  private PropertiesHandler propertiesHandler = PropertiesHandler.getInstance();
  private DrawRunnable drawRunnable;
  private UpdateRunnable updateRunnable;
  private Thread drawThread;
  private Thread updateThread;

  /**
   * Constructor for the Application.
   *
   * @throws IOException Thrown when properties file can not be read
   */
  public Application() throws IOException {
    super();
    Logger.info("Starting application...");
  }

  /**
   * Starts the application.
   *
   * @throws IOException Thrown when resource file containing properties can not be read
   */
  public void start() throws IOException {

    // Initialize width and height
    Integer width = Integer.valueOf(propertiesHandler.getProperty("application-width"));
    Integer height = Integer.valueOf(propertiesHandler.getProperty("application-height"));
    setResizable(false);
    setSize(width, height);
    Logger.info("Initialized with width: " + width + " and height: " + height);

    // Window title
    String name = propertiesHandler.getProperty("application-name");
    setTitle(name);

    // Initialize systems and fields
    scoreSystem = ScoreSystem.getInstance();
    lifeSystem = LifeSystem.getInstance();
    board = new Board();
    add(board);

    addCloseHandler();

    setLocationRelativeTo(null);
    setVisible(true);

    startRunnables();

    AudioSystem.start();
    AudioSystem.changeLoopingSound("/sounds/bg.mp3");

    Logger.info("Application started successfully!");
  }

  private void startRunnables() {
    updateRunnable = new UpdateRunnable(board, scoreSystem, lifeSystem);
    updateThread = new Thread(updateRunnable);
    Logger.info("Starting update Thread: " + updateThread);
    updateThread.start();

    drawRunnable = new DrawRunnable(board);
    drawThread = new Thread(drawRunnable);
    Logger.info("Starting draw Thread: " + drawThread);
    drawThread.start();
  }

  private void addCloseHandler() {
    JFrame frame = this;
    frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent e) {
        if (JOptionPane.showConfirmDialog(frame,
            "Are you sure you want to exit the game?", "Closing",
            JOptionPane.YES_NO_OPTION,
            JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
          close();
        } else {
          frame.setVisible(true);
        }
      }
    });
  }

  /**
   * Main method of the application.
   *
   * @param args Command line arguments
   */
  public static void main(String[] args) {
    LoggerTypes type = LoggerTypes.INFO;
    if (args.length > 0) {
      String loggerLevel = args[0].toUpperCase();
      try {
        type = LoggerTypes.valueOf(loggerLevel);
      } catch (IllegalArgumentException ex) {
        Logger.warning("Invalid first command line argument: " + loggerLevel);
        Logger.warning("Valid arguments are:");
        for (LoggerTypes loggerType : LoggerTypes.values()) {
          Logger.warning("  " + loggerType);
        }
      }
    }
    Logger.setLevel(type);

    EventQueue.invokeLater(() -> {
      app = null;
      try {
        app = new Application();
        app.start();
      } catch (IOException ex) {
        Logger.error(ex.getMessage(), ex);
      }
    });
  }

  /**
   * Closes the application.
   */
  public void close() {
    this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    Logger.info("Shutting down. Goodbye!");
    AudioSystem.stop();

    updateRunnable.terminate();
    try {
      updateThread.join(1000);
    } catch (InterruptedException ex) {
      Logger.error("Update Thread interrupted on close", ex);
    }

    drawRunnable.terminate();
    try {
      drawThread.join(1000);
    } catch (InterruptedException ex) {
      Logger.error("Draw Thread interrupted on close", ex);
    }

    Logger.getInstance().flush();
    this.dispose();
  }
}

@@ -28,16 +28,60 @@
 .pang;%0A%0A
+import lombok.Getter;%0Aimport lombok.Setter;%0A
 import n
@@ -603,17 +603,16 @@
 ashMap;%0A
-%0A
 import j
@@ -703,53 +703,8 @@
 s;%0A%0A
-import lombok.Getter;%0Aimport lombok.Setter;%0A%0A
 /**%0A
@@ -3396,16 +3396,17 @@
 wEvent e
+x
 ) %7B%0A    

Fix checkstyle in Application