package engine;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
import javax.swing.JPanel;
import _texture.loader.TextureLoader;
import _texture.loader.Textures;
import fileIO.Saving;
import fonts.fonts;
import hud.Clock;
import hud.Map;
import hud.RightClickMenu;
import hud.SecondaryHUD;
import hud.SecondaryHUD.TAB;
import hud.constructionmenu.ConstructionMenu;
import hud.inventory.Inventory;
import hud.inventory.InventorySlot;
import interfaces.Colors;
import interfaces.Mob;
import interfaces.Python;
import lighting.PlayerLightPosUpdater;
import lighting.RadialLight;
import player.HealthAndDurability;
import player.Player;
import utils.Runner;
import utils.Stack;
import utils.ThreadPool;
import utils.Utils;
import world.Tile;
import worldGen.WorldGenerator;

/**  ------------- Nihilne ------------
    *** @author Mitch Weaver - 2016 ***
      ~~~~ All Rights Reserved ~~~~ */

/*  MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMM?  .  MMMMMMMMMMMMMMMM..MMM??MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMM........ MMMMMMMMMMMD .......MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMM ..........,MMMMMMMM.........MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMI.,MMMM8.....,MMMMMM. N?.... MMMMMMMMMM  OMMM.  .MMMMMMMMMMMMMMMMMMMMMMM . MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMM?.....=MMMM.,MMMM.:MMMMMMMMMMMMM .. ..7MMMMMMMMMMMMMMMMMMMMM. M...MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMM? M....DMMMMMMMMM ~MMMMMMMMMMMMMM .. MMMMMMMMMMMMMMMMMMMMMMMM....MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMM? MZ....NMMMMMMMM ~MMMMMM NMMMMMM....MMMMMMMMMMMMMMMN.MMMMMMM....MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMM? MM......  ..... ~MMMM...... MMM ...MMMMMMMMMMMMMM....   MMM...:MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMM..: MMM.....MMMMMMM.~MMMI=,....?MMM ...MMMMMMMMMMMMM = ...,7MMM...:MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMM+ .... MMMM....7MMMMMM ~MMMMMMNMMMMMMM ...MMMMMMMMMMMMMMMMNMMMMMMM...:MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMM... .. MMMM8....MMMMMM ~MMMMM..MMMMMMM ...MMMMMM7MMMMMMMM. MMMMMMM...:MMMMMM8MMMMMM,,MMMMMMMMMMMMMM. MMMMMMMMMM
	MMMMMMMMM..MMM  MMMMM,....MMMMM.~MMMM ..  . MMM ...MMMM . DMMMMM8... . .MMM...:MMMM....MMM~ ...:MMMMMMMMMM. ...MMMMMMMMM
	MMMMMMMMM,MMMM? MMMMMM.... MMMM ~MMD,......:MMM ...MMM ......=M .......~MMM...:MMN. ...NM  ....... MM . ..M7....MMMMMMMM
	MMMMMMMMMMMMMM? MMMMMMM ...:MMM.~MMMMM...=MMMMM ...M. M .....MMMMM ..MMMMMM...:MMMMI.... NMMM... MMMMM....MMM....MMMMMMM
	MMMMMMMMMMMMMM?.M= ...$N ...MMM.~MMMMM...=MMMMM .. .=MMMO...MMMMMM ..MMMMMM...:MMMMM....MMMMM...~MMMMM ...MMM.. ,MMMMMMM
	MMMMMMMMMMMMMM?. MMMMMO......MM.~MMMMM...=MMMMM ..,MMMMMO...MMMMMM ..MMMMMM...:MMMMM...:MMMMM...~MMMMM ...MMM..MMMMMMMMM
	MMMMMMMMMMMMMM? MMMMMMMMM.....M.~MMMMM...=MMMMM ...MMMMMO...MMMMMM ..MMMMMM...:MMMMM...:MMMMM...~MMMMM ...M$ $MMMMMMMMMM
	MMMMMMMMMMMMMM? MMMMMMMMMM ...  ~MMMMM...=MMMMM ...MMMMMO...MMMMMM ..MMMMMM...:MMMMM...:MMMMM...~MMMMM .... MMMMMMMMMMMM
	MMMMMMMMMMMMMM? MMMMMM  MMM.....~MMMMM...=MMMMM ...MMMMMO...MMMMMM ..MMMMMM...:MMMMM...:MMMMM...~MMMMM ...MMMMMMMMMMMMMM
	MMMMMMMMMMM.......... $MMMMM....~MMMMM....MMMMM .. MMMMMO...NMMMMM... MMMMM...~MMMMM ..:MMMMM...~MMMMM....MMMMMM8MMMMMMM
	MMMMMMMMM............MMMMMMMM...:MMMMM ....~MD.....=..MMZ... MMMMM ... =MM.....M .M .....M.MM ...778M ..... MM  IMMMMMMM
	MMMMMMMZ . .MM~ ...MMMMMMMMMMM...MMMMMM....MM..,....MMMM . .NMMMMMI... MN.......MM......  MMMD....+M  ........MMMMMMMMMM
	MMMMMMM =MMMMMMMMMMMMMMMMMMMMMM  MMMMMMM MMMMMMMM.MMMMM  MMMMMMMMMM7 MMMMMMM=.MMMMMMMMM MMMMMMM.MMMMMMMMM  =MMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM..MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM. MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM?.,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM.. MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
	MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM */

public final class Game extends Canvas implements Runnable, Python, Colors {
	private static final long serialVersionUID = 1337;
	public static final String version = "Pre-Alpha --- Week 10";

	// Variable Declaration
	public static final int TILE_SIZE = 32, threads = ThreadPool.getNumProcessors();
	public static double MAX_SCALEFACTOR = 5, MIN_SCALEFACTOR = 1;
	public static int WIDTH = 1280, HEIGHT = 720; /* will change in settings, this is default */
	public static double FPS_CAP = 60.0, frameLimit = (Utils.second / FPS_CAP), scaleFactor = 2.0;
	private static boolean running;
	public static int rotation = 1; // 1 = N, 2 = NW, 3 = W, 4 = SW, 5 = S, 6 = SE, 7 = E, 8 = NE
	private static int frames = 0, ticks = 0, FPS = 0, TPS = 0;
	private static long timeTakenForInit;
	public static volatile boolean init;

	// Object creation
	public static final Game game = new Game();
	public Render renderer; /* can't be static due to Canvas */
	public static Menu menu;
	public static volatile Settings settings; /* volatile just */
	public static volatile Saving saving; /* to be safe */
	public static JFrame frame;
	public static JPanel panel;
	public static ThreadPool pool;
	public static STATE state;
	public static MouseInput mouse;

	public static boolean settingsChanged = true, fullscreen = true, displayFPS = true, showEnemyHealthBars = true;

	public static enum STATE { GAME, MENU, SETTINGS, ABOUT, LOADING, PAUSED, MAP, GAMEO_OVER, DEVELOPER_CONSOLE; }

	/****************************************************** DEBUG *********************************************************************************/
	public static boolean drawCollisionBounds, noClip, dontShowDebris, drawCurrentTime, drawInventoryBounds, drawCenterLines, dontDrawPlayer, godMode, noHunger,
			drawResourceNumbers, drawMobTerritoryZones, drawMobAggroRadii, alwaysDawn, alwaysDay, alwaysDusk, alwaysNight,
			drawPlayerAttackBounds, drawRightClickBounds, drawMobAttackBounds;
	public static int clockSpeed = 3;
	/*************************************************** END DEBUG ********************************************************************************/

	public static final void main(String[] args) {
		game.init();
	}

	public final static void tick() {
			ticks++;

			if (state == STATE.GAME) {

				Handler.tick();
				for (int i = 0; i < clockSpeed; i++) Clock.tick();

				ConstructionMenu.tick();
				PlayerLightPosUpdater.tick();
				HealthAndDurability.tick();
				RightClickMenu.tick();

			} else if(state == STATE.MAP) Map.tick();

			KeyInput.tick();
			MouseInput.tick();
	}

	private static BufferStrategy bs; private static Graphics g;
	private static Graphics2D g2d, InventoryRenderer, InventorySlotRenderer, ClockRenderer, SecondaryHUDRenderer, constructionMenuRenderer, debugRenderer;
	public static boolean justRotated, justCameFromSettings;
	public static int tempRotation = 1;
	private final class Render implements Runnable {
		private boolean rendering; private int menuSplashTimer = 0;

		public final void run() { if(!rendering) render(); }

		public final void render() {
			if (menuSplashTimer > 0 && Game.state != STATE.MENU) menuSplashTimer = 0;
			rendering = true;
			try {

				if (bs == null) game.createBufferStrategy(3);
				bs = game.getBufferStrategy();
				if (!(bs.getDrawGraphics() == null)) g = bs.getDrawGraphics();
				if (!(g == null)) {
					g2d = (Graphics2D) g;
					InventoryRenderer = (Graphics2D) g2d.create();
					ClockRenderer = (Graphics2D) g2d.create();
					InventorySlotRenderer = (Graphics2D) g2d.create();
					SecondaryHUDRenderer = (Graphics2D) g2d.create();
					debugRenderer = (Graphics2D) g2d.create();
					constructionMenuRenderer = (Graphics2D) g2d.create();
				}

				if (state == STATE.GAME) {

					if(!WorldGenerator.WORLD_GENERATED) {
							if(WorldGenerator.beginTileCreation)
							try{ int total = 0;
								 for(final Stack<Tile> i : WorldGenerator.workerStacks) total+= i.size();
								 WorldGenerator.percentage = 100 - (100 * (total / WorldGenerator.stackSize));
							}catch(NullPointerException npe) { WorldGenerator.percentage = 100; }

							final int color = Utils.clamp((int)(255*(WorldGenerator.percentage/100)/1.3), 0, 255);

							g2d.setColor(new Color(color, color, color));
							g2d.fillRect(0, 0, getWidth(), getHeight());
							g2d.setColor(new Color(255 - color, 0, 0));
							g2d.setFont(fonts.Gregorian32Bold);
							g2d.drawString("LOADING", Game.game.getWidth()/2 - 100, Game.game.getHeight()/2);
							g2d.setFont(fonts.Gregorian48Bold);
							g2d.drawString(Utils.df.format(WorldGenerator.percentage)+"%", Game.game.getWidth()/2 - 78, Game.game.getHeight()/2 + 60);
					} else {

					Camera.tick();
					Inventory.tick();

					// draw the base layer
					g2d.setColor(Color.black);
					g2d.fillRect(0, 0, getWidth(), getHeight());

					g2d.rotate(Utils._45 * (rotation - 1));
					g2d.scale(scaleFactor, scaleFactor);
					g2d.translate(Camera.x, Camera.y);
					Handler.render(g2d); /* Render game objects */
					g2d.translate(-Camera.x, -Camera.y);

					RadialLight.render(debugRenderer);
					}
				} else if (state == STATE.PAUSED) {
					PauseMenu.tick();
					if (justCameFromSettings) {
						g2d.setColor(Color.black);
						justCameFromSettings = false;
						Inventory.changeSettings();
					} else g2d.setColor(pause_menu_alpha);
					g2d.fillRect(0, 0, getWidth() + 50, getHeight() + 50);
					PauseMenu.render(g2d);
				} else if (state == STATE.MENU) {
					menu.tick();
					if (menuSplashTimer <= 0) {
						g2d.drawImage(Textures.splashSheet[0].getScaledInstance(getWidth(), getHeight(), 1), 0, 0, null);
						g2d.setColor(Color.red);
						g2d.setFont(fonts.Gregorian26);
						g2d.drawString(version, getWidth() / 17, getHeight() / 1.1f);
						menuSplashTimer = 20;
					} else menuSplashTimer--;

					menu.render(g2d);
				} else if (state == STATE.SETTINGS) {
					settings.tick();
					g2d.setColor(Color.black);
					g2d.fillRect(0, 0, getWidth() + 50, getHeight() + 50);
					settings.render(g2d);
				} else if (state == STATE.MAP) {
					Map.render(g2d);
				} else if(state == STATE.DEVELOPER_CONSOLE) {
					DeveloperConsole.render(g2d);
				}

				if(WorldGenerator.WORLD_GENERATED){
				// Draw non-scaling / rotating things:
				if (displayFPS) { /* can be rendered with any of the other g2d's, just can't be rotated */
					InventoryRenderer.setColor(Color.green);
					InventoryRenderer.setFont(fonts.fallbackFont);
					InventoryRenderer.drawString(("FPS: " + (int) FPS + " TICKS: " + (int) TPS), (int) (getWidth() * 0.03f), (int) (getHeight() * 0.03f));



					// XXX: HERE IS SOEMTHING TO DELETE!
					InventoryRenderer.setColor(Color.red);
					InventoryRenderer.drawString(rotation + "", (int) (getWidth() * 0.4f), (int) (getHeight() * 0.04f));
					InventoryRenderer.setFont(fonts.Gregorian32Bold);
					InventoryRenderer.drawString("Player Health: " + Player.health, (int)(getWidth() * 0.7f), (int)(getHeight() * 0.04f));
				}

				if (drawCenterLines) {
					InventoryRenderer.setColor(Color.white);
					InventoryRenderer.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
					InventoryRenderer.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
				}

				if (Game.state == STATE.GAME) {
					InventoryRenderer.translate(getWidth() / 2, getHeight() - Inventory.INVENTORY_HEIGHT / 2);
					InventoryRenderer.translate(-Inventory.INVENTORY_WIDTH / 2, -Inventory.INVENTORY_HEIGHT / 2);
					Inventory.render(InventoryRenderer, InventorySlotRenderer);

					ClockRenderer.translate(getWidth() / 1.08 - Clock.width / 2, getHeight() / 7.75 - Clock.height / 2);
					Clock.render(ClockRenderer);

					SecondaryHUD.render(SecondaryHUDRenderer);
					ConstructionMenu.render(constructionMenuRenderer);
				}

				RightClickMenu.render(debugRenderer);

				if (drawInventoryBounds && Game.state == STATE.GAME) {

					debugRenderer.setColor(Color.pink);
					debugRenderer.draw(Inventory.INVENTORY_BOUNDS);
					if (SecondaryHUD.tab == TAB.MINIMIZED) debugRenderer.draw(SecondaryHUD.minimizedBounds);
					else debugRenderer.draw(SecondaryHUD.maximizedBounds);

					if (SecondaryHUD.tab == TAB.EQUIPMENT) {
						for (final InventorySlot i : Player.equipmentSlots) {
							debugRenderer.setColor(Color.GREEN);
							debugRenderer.drawRect(i.bounds.x, i.bounds.y, i.bounds.width, i.bounds.height);
							debugRenderer.setColor(Color.RED);
							debugRenderer.drawString(Integer.toString(i.slotNumber), i.bounds.x + i.bounds.width / 2, i.bounds.y + i.bounds.height / 2);
						}
					}
					for (final InventorySlot i : Player.inventorySlots) {
						debugRenderer.setColor(Color.orange);
						debugRenderer.drawRect(i.bounds.x, i.bounds.y, Inventory.SLOT_WIDTH, Inventory.SLOT_HEIGHT);
						debugRenderer.setColor(Color.RED);
						debugRenderer.drawString(Integer.toString(i.slotNumber), i.bounds.x + Inventory.SLOT_WIDTH / 2, i.bounds.y + Inventory.SLOT_HEIGHT / 2);
					}
					for (final InventorySlot i : Player.tabSlots) {
						debugRenderer.setColor(Color.MAGENTA);
						debugRenderer.drawRect(i.bounds.x, i.bounds.y, i.bounds.width, i.bounds.height);
						debugRenderer.setColor(Color.RED);
						debugRenderer.drawString(Integer.toString(i.slotNumber), i.bounds.x + i.bounds.width / 2, i.bounds.y + i.bounds.height / 2);
					}
				}}


				debugRenderer.dispose();
				SecondaryHUDRenderer.dispose();
				InventorySlotRenderer.dispose();
				InventoryRenderer.dispose();
				ClockRenderer.dispose();
				g.dispose();
				g2d.dispose();
				bs.show();

				frames++; /**** update fps ****/

				rotation = tempRotation;
			} catch (Exception e){
				e.printStackTrace();
				debugRenderer.dispose();
				SecondaryHUDRenderer.dispose();
				InventorySlotRenderer.dispose();
				InventoryRenderer.dispose();
				ClockRenderer.dispose();
				g.dispose();
				g2d.dispose();
			}
			rendering = false;
		}
	}

	/***************************************************************************************************************************/

	private final void init(){
		if (!running && !init) {
			timeTakenForInit = System.nanoTime();
			init = running = true;

			pool = new ThreadPool(threads);

			SplashScreen.init(); TextureLoader.initSplashSheet(); fonts.init();
			pool.runTask(SplashScreen.splashRender);


			// put all stuff that doesn't need jython + can be multithreaded here:
			pool.runTask(  new Runner(() ->  {

				System.err.println(Utils.OS);

				renderer = new Render();
				Clock.init();
				RightClickMenu.init();
				SecondaryHUD.init();
				ConstructionMenu.init();

				setFocusTraversalKeysEnabled(false); /* NOTE YOU NEED THIS FOR TAB KEY TO WORK! */
				frame = new JFrame("\"Nihilne\" ---- " + version);
				frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
				frame.setResizable(false);
				frame.setLocationRelativeTo(null);
				mouse = new MouseInput();
				game.addMouseListener(mouse);
				game.addMouseWheelListener(mouse);
				game.addMouseMotionListener(mouse);
				panel = new JPanel(); /* add keybindings to this panel, Canvas does not support them */
				frame.add(panel); frame.add(game); frame.pack();

				DeveloperConsole.init();

				KeyInput.init();
				menu = new Menu();
				settings = new Settings();
				saving = new Saving();
				menu.init();
				TextureLoader.init();
				Tile.init();

			}));


			initJython();

			Inventory.init(); Inventory.changeSettings();

			settings.init(); Settings.applySettings(); settings.cleanUp(); SplashScreen.cleanUp();
			state = STATE.MENU;
			System.gc();
			init = false;
			System.out.println("Time taken for init: " + Utils.df.format((System.nanoTime() - timeTakenForInit) / Utils.second) + " seconds" + "\n" + "Welcome to Nihilne!");
			pool.runTask(game);
		}
	}

	private static final void initJython() {
		try{ final long then = System.nanoTime();

			Utils.initializePySystem();

			// CUSTOM CLASSES
			pi.exec("from engine import Game, Camera, DeveloperConsole, Handler, KeyInput, MouseInput");
			pi.exec("from hud import SecondaryHUD, RightClickMenu");
			pi.exec("from hud.inventory import Inventory, InventorySlot, InvenHelper");
			pi.exec("from player import Player, PlayerAttackSequence, HealthAndDurability");
			pi.exec("from world import DroppedItem");
			pi.exec("from mobs import Rabbit, Goblin, DROP_TABLES");

			// INTERFACES
			pi.exec("from interfaces import Mob, Biomes, ItemIDs, Colors, Directions, PlayerAnimationIDs");
			pi.exec("from interfaces.Mob import ANIMATION");
			pi.exec("from interfaces.ItemIDs import *");
			pi.exec("from interfaces.MobData import *");

			// UTILS JAR
			pi.exec("from utils import Utils, Point2f, Stack, Circle, Animation");

			// LOAD SCRIPTS
			pi.exec(Utils.readTextFileAsString("/inventory.py"));
			Mob.initAllPyScripts();

			pi.exec("print " + "'Jython / Python initialized --- Time taken: " + Utils.df.format((System.nanoTime() - then) / Utils.second) + " seconds'");
		} catch (Exception e) { e.printStackTrace(); System.exit(1); }
	}

	public final void run() {
		long now = System.nanoTime(), frameThen = System.nanoTime(), tickThen = System.nanoTime(), timer = System.currentTimeMillis();
		final double tickLimit = Utils.second / 60.0;

		frame.setVisible(true);

		while (running) {
			now = System.nanoTime();

			if (WorldGenerator.WORLD_GENERATED){
				if(now - tickThen > tickLimit) {
					tick(); tickThen = now = System.nanoTime();
				}

				if (now - frameThen > frameLimit) {
					frameThen = now;

						pool.runTask(renderer);

						if (displayFPS && System.currentTimeMillis() - timer > 1000) {
							FPS = frames; TPS = ticks; frames = ticks = 0;
							timer += 1000;
						}
				}

//				try { Thread.sleep(5); } catch(InterruptedException e) {}
			} else try{ renderer.render(); Thread.sleep(20); } catch (InterruptedException e) { }
		}
	}

	public static synchronized final void makeFullScreen(){
		final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); /* get monitor */
		final GraphicsDevice vc = ge.getDefaultScreenDevice(); /* get video card */
		WIDTH = (int) vc.getDefaultConfiguration().getBounds().getWidth(); /* get width and height of screen */
		HEIGHT = (int) vc.getDefaultConfiguration().getBounds().getHeight();
		frame.setSize(WIDTH, HEIGHT);
		/* can't undecorate unless frame is made undisplayable. */
		if (frame.isDisplayable()) frame.dispose();
		frame.setUndecorated(true);
		vc.setFullScreenWindow(frame); /* also makes display displayable again */
	}

	public static synchronized final void undoFullScreen(){ frame.dispose(); frame.setUndecorated(false); frame.setVisible(true); }

	public synchronized final static void cleanUp() {
		try {	frame.dispose();
				pi.cleanup();
				pi.close();
				Handler.cleanUp();
				Map.cleanUp();
				PauseMenu.cleanUp();
				KeyInput.cleanUp();
				settings.cleanUp();
				menu.cleanUp();
				RightClickMenu.cleanUp();
				SecondaryHUD.cleanUp();
				Inventory.cleanUp();
				System.gc();
				System.out.println("~~~             Goodbye!             ~~~");
		} catch (Exception e) { e.printStackTrace(); }
		running = false;
		System.exit(0);
	}
}
