import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import java.util.*;

public class Template4k extends JFrame {

	/*
	* This is a 4k template that uses an Image drawn upon a JFrame
	* for graphics.
	*
	* Keys are enabled and states are saved in the keys array.
	* MouseMotion events are also enabled. The mouse coordinates
	* are stored in mouseX and mouseY. The current state of each
	* mouse button is saved in the mouse array.
	*
	* Make sure to remove any event handling you don't need, as it
	* uses some space. Also note that mouseClicked and keyTyped
	* are not handled; I suggest finding state changes in the game
	* loop instead.
	*
	* Events are handled in the same way as Markus Persson did for
	* Miners4k.
	*
	* Loop delay and timing by oNyx.
	*
	* Note that the enableEvents line does not seem neccessary (active
	* per default). It's there because it looks nice.
	*
	* Good luck!
	*
	* //Morre
	*/

	//========================= CONSTANTS AND GLOBAL VARIABLES =============================

	/*
	* WIDTH, HEIGHT specifies frame size.
	*/
	private static final int WIDTH = 640;
	private static final int HEIGHT = 480;

	/*
	* Preferred frame time in milliseconds. Default is 16, which gives
	* 1000/16 ~= 60 fps.
	*/
	private static final int FRAME_TIME = 16;

	/*
	* Says whether or not a session is running. If this is set to true,
	* the outer loop will restart in the next iteration, restarting game.
	*/
	private boolean running;

	/*
	* Key state array. Indexed with the KeyEvent key constants.
	*/
	private boolean keys[] = new boolean[1024];

	/*
	* Mouse button state array. Indexed with the mouse button numbers.
	*/
	private boolean mouse[] = new boolean[8];
	private int mouseX = 0;
	private int mouseY = 0;

	/*
	* Quality settings boolean, toggled with Q. Defaults to false.
	*/
	private boolean quality = true;

	//============================== MAIN METHOD ===================================

	public static void main(String[] args)	{
		new Template4k();
	}

	//============================== CONSTRUCTOR ===================================

	public Template4k() {

		/*
		* Initialize frame, set dimensions, etc.
		*/
		setSize(new Dimension(WIDTH+getInsets().right*getInsets().left,HEIGHT+getInsets().top+getInsets().bottom));
		setLocation(100,100);
		setResizable(false);
		setTitle("Template4k");
		enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
		setVisible(true);

	//============================== OUTER LOOP  ===================================
	//				(runs each time game is played or restarted)

		while(true) {

			/*
			* ileft and itop are the insets of the JFrame; they might be needed
			* if you draw stuff directly to the gFrame (JFrame's) graphics context.
			* They can also be used for translating the buffer image when drawing
			* onto gFrame, if you desire to see the top left corner of the buffer.
			*/
			int ileft = getInsets().left;
			int itop = getInsets().top;

			/*
			* Create graphics contexts. The off image is drawn to the JFrame,
			* making it possible to do pixel operations for the graphics. Also,
			* drawing to g (the buffer's graphics context) will make sure that
			* what you draw is double-buffered.
			*/
			Image off = createImage(WIDTH, HEIGHT);

			/*
			* Initialize timing and loop logic.
			*/
			running = true;
			long lastFrame=System.currentTimeMillis();
			float yield=10000f;
			float frameAverage=(float)FRAME_TIME;

			/*
			* Initialize variables for the game.
			* fcount is a variable that increases each frame.
			*/
			int fcount = 0;

	//============================== INNER LOOP  ===================================
	//							(runs each frame)
			while(running)	{

				Graphics2D gFrame = (Graphics2D)getGraphics();
				Graphics2D g = (Graphics2D)off.getGraphics();

				/*
				* Increase frame count.
				*/
				fcount++;

				/*
				* Clear the off buffer.
				*/
				g.setColor(Color.white);
				g.fillRect(0,0,WIDTH,HEIGHT);

				/*
				* Draw the off buffer to the frame's graphics context.
				*/
				gFrame.drawImage(off,0,0,null);

				g.dispose();
				gFrame.dispose();

				/*
				* Loop time and timing logic.
				*/
				long timeNow = System.currentTimeMillis();
				//rolling average for the last 20 time inputs
				frameAverage = (frameAverage * 20 + (timeNow - lastFrame)) / 21;
				lastFrame=timeNow;
				//0.1f = damping value
				//and that +0.05f bit is for ensuring that it can grow faster after it ran flat out for a while
				yield+=yield*(((float)FRAME_TIME/frameAverage)-1)*0.1f+0.05f;
				for(int i=0;i<yield;i++)
					Thread.yield();

			}

		}
	}

	/*
	* Handle window closing events and key/mouse events.
	*/
    public void processEvent(AWTEvent e)
    {
        switch (e.getID())
        {
            case WindowEvent.WINDOW_CLOSING:
				System.exit(0);
				break;
            case KeyEvent.KEY_PRESSED:
				keys[((KeyEvent)e).getKeyCode()] = true;
            	int k = ((KeyEvent)e).getKeyCode();
				if(k == KeyEvent.VK_Q) {
					quality = !quality;
				}
				if(k == KeyEvent.VK_ESCAPE) {
					System.exit(0);
				}
				if(k == KeyEvent.VK_R) {
					running = false;
				}
				break;
            case KeyEvent.KEY_RELEASED:
				keys[((KeyEvent)e).getKeyCode()] = false;
				break;
			case MouseEvent.MOUSE_MOVED:
			case MouseEvent.MOUSE_DRAGGED:
				mouseX = ((MouseEvent)e).getX();
				mouseY = ((MouseEvent)e).getY();
				break;
			case MouseEvent.MOUSE_PRESSED:
				mouse[((MouseEvent)e).getButton()] = true;
				break;
			case MouseEvent.MOUSE_RELEASED:
				mouse[((MouseEvent)e).getButton()] = false;
				break;
        }
    }

}
