package renderEngine;

import org.lwjgl.*;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
import org.lwjgl.system.*;

import java.nio.*;

import javax.vecmath.Vector2f;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryStack.*;
import static org.lwjgl.system.MemoryUtil.*;

/**
 * This class contains all the methods needed to set-up, maintain, and close a LWJGL display.
 * 
 * @author Karl
 *
 */
public class DisplayManager {

	private static final int WIDTH = 1280;
	private static final int HEIGHT = 720;
	private static final int FPS_CAP = 60;
	private static final String TITLE = "Our First Display";
	private DisplayManagerDraw drawer = null;
	public void setDrawer(DisplayManagerDraw drawer) {
		this.drawer = drawer;
	}
//	/**
//	 * Creates a display window on which we can render our game. The dimensions
//	 * of the window are determined by setting the display mode. By using
//	 * "glViewport" we tell OpenGL which part of the window we want to render
//	 * our game onto. We indicated that we want to use the entire window.
//	 */
//	public static void createDisplay() {
//		ContextAttribs attribs = new ContextAttribs(3, 2).withForwardCompatible(true).withProfileCore(true);
//		try {
//			Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
//			Display.create(new PixelFormat(), attribs);
//			Display.setTitle(TITLE);
//		} catch (LWJGLException e) {
//			e.printStackTrace();
//		}
//		GL11.glViewport(0, 0, WIDTH, HEIGHT);
//	}
//
//	/**
//	 * This method is used to update the display at the end of every frame. When
//	 * we have set up a rendering process this method will display whatever
//	 * we've been rendering onto the screen. The "sync" method is used here to
//	 * cap the frame rate. Without this the computer would just try to run the
//	 * game as fast as it possibly can, doing more work than it needs to.
//	 */
//	public static void updateDisplay() {
//		Display.sync(FPS_CAP);
//		Display.update();
//	}
//
//	/**
//	 * This closes the window when the game is closed.
//	 */
//	public static void closeDisplay() {
//		Display.destroy();
//	}

	// The window handle
	private static long window;

	public static Vector2f getSize() {
		return new Vector2f(WIDTH, HEIGHT);
	}

	
	public void init() {
		System.out.println("Hello LWJGL " + Version.getVersion() + "!");

		initWindows();
		// This line is critical for LWJGL's interoperation with GLFW's
		// OpenGL context, or any context that is managed externally.
		// LWJGL detects the context that is current in the current thread,
		// creates the GLCapabilities instance and makes the OpenGL
		// bindings available for use.
		GL.createCapabilities();
		// Set the clear color
		glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
		glViewport(0, 0, WIDTH, HEIGHT);
	}
	public void unInit() {
	
		// Free the window callbacks and destroy the window
		glfwFreeCallbacks(window);
		glfwDestroyWindow(window);

		// Terminate GLFW and free the error callback
		glfwTerminate();
		glfwSetErrorCallback(null).free();
	}
	private static boolean valueS = false;
	private static boolean valueZ = false;
	private static boolean valueQ = false;
	private static boolean valueD = false;
	private static boolean valueA = false;
	private static boolean valueW = false;
	public static boolean isKeyDown(char value) {
		if (value == 's') {
			return valueS;
		}
		if (value == 'z') {
			return valueZ;
		}
		if (value == 'q') {
			return valueQ;
		}
		if (value == 'd') {
			return valueD;
		}
		if (value == 'w') {
			return valueW;
		}
		if (value == 'a') {
			return valueA;
		}
		return false;
	}

	private void initWindows() {
		// Setup an error callback. The default implementation
		// will print the error message in System.err.
		GLFWErrorCallback.createPrint(System.err).set();

		// Initialize GLFW. Most GLFW functions will not work before doing this.
		if ( !glfwInit() ) {
			throw new IllegalStateException("Unable to initialize GLFW");
		}
		// Configure GLFW
		glfwDefaultWindowHints(); // optional, the current window hints are already the default
		glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // the window will stay hidden after creation
		glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // the window will be resizable

		// Create the window
		window = glfwCreateWindow(WIDTH, HEIGHT, TITLE, NULL, NULL);
		if ( window == NULL )
			throw new RuntimeException("Failed to create the GLFW window");

		// Setup a key callback. It will be called every time a key is pressed, repeated or released.
		glfwSetKeyCallback(window, (window, key, scancode, action, mods) -> {
			if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE ) {
				glfwSetWindowShouldClose(window, true); // We will detect this in the rendering loop
			}
			if ( key == GLFW_KEY_Z ) {
				if (action == GLFW_PRESS ) {
					System.out.println("Key Z is pressed");
					valueZ = true;
				} else if (action == GLFW_RELEASE ) {
					System.out.println("Key Z is release");
					valueZ = false;
				}
			}
			if ( key == GLFW_KEY_S ) {
				if (action == GLFW_PRESS ) {
					System.out.println("Key S is pressed");
					valueS = true;
				} else if (action == GLFW_RELEASE ) {
					System.out.println("Key S is release");
					valueS = false;
				}
			}
			if ( key == GLFW_KEY_Q ) {
				if (action == GLFW_PRESS ) {
					System.out.println("Key Q is pressed");
					valueQ = true;
				} else if (action == GLFW_RELEASE ) {
					System.out.println("Key Q is release");
					valueQ = false;
				}
			}
			if ( key == GLFW_KEY_D ) {
				if (action == GLFW_PRESS ) {
					System.out.println("Key D is pressed");
					valueD = true;
				} else if (action == GLFW_RELEASE ) {
					System.out.println("Key D is release");
					valueD = false;
				}
			}
			if ( key == GLFW_KEY_A ) {
				if (action == GLFW_PRESS ) {
					System.out.println("Key A is pressed");
					valueA = true;
				} else if (action == GLFW_RELEASE ) {
					System.out.println("Key A is release");
					valueA = false;
				}
			}
			if ( key == GLFW_KEY_W ) {
				if (action == GLFW_PRESS ) {
					System.out.println("Key W is pressed");
					valueW = true;
				} else if (action == GLFW_RELEASE ) {
					System.out.println("Key W is release");
					valueW = false;
				}
			}
		});

		// Get the thread stack and push a new frame
		try ( MemoryStack stack = stackPush() ) {
			IntBuffer pWidth = stack.mallocInt(1); // int*
			IntBuffer pHeight = stack.mallocInt(1); // int*

			// Get the window size passed to glfwCreateWindow
			glfwGetWindowSize(window, pWidth, pHeight);

			// Get the resolution of the primary monitor
			GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());

			// Center the window
			glfwSetWindowPos(
				window,
				(vidmode.width() - pWidth.get(0)) / 2,
				(vidmode.height() - pHeight.get(0)) / 2
			);
		} // the stack frame is popped automatically

		// Make the OpenGL context current
		glfwMakeContextCurrent(window);
		// Enable v-sync
		glfwSwapInterval(1);

		// Make the window visible
		glfwShowWindow(window);
	}

	public void loop() {

		// Run the rendering loop until the user has attempted to close
		// the window or has pressed the ESCAPE key.
		while ( !glfwWindowShouldClose(window) ) {
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
			if (this.drawer != null) {
				this.drawer.draw();
			}
			glfwSwapBuffers(window); // swap the color buffers

			// Poll for window events. The key callback above will only be
			// invoked during this call.
			glfwPollEvents();
		}
	}

}
