module dflsample;

import dsfml.system.all;
import dsfml.window.all;

// DFL and Derelict must be present.
import dfl.all;

import Derelict.opengl.gl;
import Derelict.opengl.glu;


// An enum for each controls methods
enum ControlMethod
{
	MOUSE,
	KEYBOARD
}

void main()
{
	DerelictGL.load();
	DerelictGLU.load();
	//Start the message loop
	Application.run(new MyForm());
}

//A simple form with a groupbox to choose input method and the openGL control
class MyForm : Form
{
	GLControl m_gl;
	GroupBox m_gbx;
	RadioButton m_rb1;
	RadioButton m_rb2;
	
	this()
	{
		m_gbx = new GroupBox();
		m_gbx.dock = DockStyle.TOP;
		m_gbx.height = 40;
		m_gbx.text = "Choose your input";
		this.controls.add(m_gbx);
		
		m_rb1 = new RadioButton();
		m_rb1.text = "Mouse";
		m_rb1.location = Point(10, 15);
		m_rb1.checked = true;
		m_rb1.click ~= &radioButtonClick;
		m_gbx.controls.add(m_rb1);
		
		m_rb2 = new RadioButton();
		m_rb2.text = "Keyboard";
		m_rb2.location = Point(m_rb1.width + 10, 15);
		m_rb2.click ~= &radioButtonClick;
		m_gbx.controls.add(m_rb2);
		
		m_gl = new GLControl();
		m_gl.dock = DockStyle.FILL;
		m_gl.controlMethod = ControlMethod.MOUSE;
		this.controls.add(m_gl);
		
		this.text = "DFL Opengl Integration Sample";
	}
	
	private void radioButtonClick(Control c, EventArgs ea)
	{
		m_gl.controlMethod(c.text == "Mouse" ? ControlMethod.MOUSE : ControlMethod.KEYBOARD);
		m_gl.focus();
	}
 
}

//Our OpenGL control
class GLControl : Control
{
	Window m_win;
	Input i;
	Timer m_timer;
	GLfloat rotx = 0.f, roty = 0.f;
	ControlMethod m_method = ControlMethod.MOUSE;
	
	this ()
	{
		super();
		this.setStyle(ControlStyles.SELECTABLE | ControlStyles.ALL_PAINTING_IN_WM_PAINT | ControlStyles.WANT_ALL_KEYS, true);
		
		//We set a timer to ensure periodic refresh of the window
		m_timer = new Timer();
		m_timer.interval(10);
		m_timer.tick ~= &this.onTick;
			
	}
	
	public void controlMethod(ControlMethod m)
	{
		m_method = m;
	}
	
	//Override of the onHandleCreated method of Control
	//integration of DSFML window in a control need a valid handle 
	protected void onHandleCreated(EventArgs ea)
	{
		super.onHandleCreated(ea);
		
		//Construction of the window
		m_win = new Window(cast(WindowHandle)this.handle);
		
		//Get the input for further use
		i = m_win.getInput();
		
		//Now that the Window is instanciated, we can start the timer.
		m_timer.start();
		
		//Some opengl initializations functions
		glClearDepth(1.f);
		glClearColor(0.f, 0.f, 0.f, 0.f);
		glEnable(GL_DEPTH_TEST);
		glDepthMask(GL_TRUE);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(90.f, 1.f, 1.f, 500.f);
	}

	//We handle Mouse leaving and entering the control to hide or show the cursor
	protected void onMouseEnter(MouseEventArgs mea)
	{
		super.onMouseEnter(mea);
		Cursor.current.hide();
	}
	
	protected void onMouseLeave(MouseEventArgs mea)
	{
		super.onMouseLeave(mea);
		Cursor.current.show();
	}

	//If the window is resize, we need to modify openGL view
	protected void onResize(EventArgs ea)
	{
		super.onResize(ea);
		glViewport(0, 0, this.width, this.height);
	}

	protected void onTick(Timer t, EventArgs ea)
	{
		draw();
	}

	protected void onPaint(PaintEventArgs pea)
	{
		super.onPaint(pea);
		draw();
	}

	private void handleKeys()
	{
		if (i.isKeyDown(KeyCode.UP))
		 rotx += 1.f;
		if (i.isKeyDown(KeyCode.DOWN))
		 rotx += -1.f;
		if (i.isKeyDown(KeyCode.LEFT))
		 roty += -1.f;
		if (i.isKeyDown(KeyCode.RIGHT))
		 roty += 1.f;

	}
	private void handleMousePos()
	{
		rotx = i.getMouseY() - (this.height / 2.0);
		roty = i.getMouseX() - (this.width / 2.0);
	}
	
	private void draw()
	{
		if (m_method == ControlMethod.KEYBOARD)
			handleKeys();
		else
			handleMousePos();

		m_win.setActive(true);
		
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glTranslatef(0.f, 0.f, -200.f);
		glRotatef(rotx, 1.f, 0.f, 0.f);
		glRotatef(roty, 0.f, 1.f, 0.f);

		glBegin(GL_QUADS);
			glColor3f(1.f, 0.f, 0.f);
			glVertex3f(-50.f, -50.f, -50.f);
			glVertex3f(-50.f,  50.f, -50.f);
			glVertex3f( 50.f,  50.f, -50.f);
			glVertex3f( 50.f, -50.f, -50.f);
			
			glColor3f(0.f, 1.f, 0.f);
			glVertex3f(-50.f, -50.f, 50.f);
			glVertex3f(-50.f,  50.f, 50.f);
			glVertex3f( 50.f,  50.f, 50.f);
			glVertex3f( 50.f, -50.f, 50.f);
		
			glColor3f(0.f, 0.f, 1.f);
			glVertex3f(-50.f, -50.f, -50.f);
			glVertex3f(-50.f,  50.f, -50.f);
			glVertex3f(-50.f,  50.f,  50.f);
			glVertex3f(-50.f, -50.f,  50.f);
		
			glColor3f(1.f, 1.f, 0.f);
			glVertex3f(50.f, -50.f, -50.f);
			glVertex3f(50.f,  50.f, -50.f);
			glVertex3f(50.f,  50.f,  50.f);
			glVertex3f(50.f, -50.f,  50.f);
		
			glColor3f(1.f, 0.f, 1.f);
			glVertex3f(-50.f, -50.f,  50.f);
			glVertex3f(-50.f, -50.f, -50.f);
			glVertex3f( 50.f, -50.f, -50.f);
			glVertex3f( 50.f, -50.f,  50.f);
		
			glColor3f(0.f, 1.f, 1.f);
			glVertex3f(-50.f, 50.f,  50.f);
			glVertex3f(-50.f, 50.f, -50.f);
			glVertex3f( 50.f, 50.f, -50.f);
			glVertex3f( 50.f, 50.f,  50.f);		
		glEnd();
				
		m_win.display();
	}
}