:- use_foreign_library(foreign(plOpenGL)).
:- use_module(library(plOpenGL)).
:- use_module(library(plGL_defs)).
:- use_module(library(plGLU_defs)).
:- use_module(library(plGLUT_defs)).
:- use_module(library(plGL)).
:- use_module(library(plGLU)).
:- use_module(library(plGLUT)).


:- dynamic texture/2,particle/17,xstrength/1,ystrength/1,zstrength/1,show_particles/1,zoom/1,rainbow/1,fullscreen/1.

width(800).
height(600).
max_particles(1000).
max_particles_age(1).
max_bounce_count(5).
show_particles(200).
rainbow(0).
r(0.8).
g(0.2).
b(0.0).
cR(0.001).
cG(0.002).
cB(0.003).
xstrength(20.0).
ystrength(20.0).
zstrength(20.0).
zoom(-10.0).
fullscreen(0).

colors(0, 1.0, 0.5, 0.5).
colors(1, 1.0, 0.75, 0.5).
colors(2, 1.0, 1.0, 0.5).
colors(3, 0.75, 1.0, 0.5).
colors(4, 0.5, 1.0, 0.5).
colors(5, 0.5, 1.0, 0.75).
colors(6, 0.5, 1.0, 1.0).
colors(7, 0.5, 0.75, 1.0).
colors(8, 0.5, 0.5, 1.0).
colors(9, 0.75, 0.5, 1.0).
colors(10, 1.0, 0.5, 1.0).
colors(11, 1.0, 0.5, 0.75).

bmp(1, 'Data/particle.bmp').
bmp(2, 'Data/plane.bmp').

/* Template of a particle struct
PARTICLE {
	  int Id,
	  float X,Y,Z;
	  float sX,sY,sZ;
	  float tX,tY,tZ;
	  float R,B,G;
	  bool Active; int Age; int MaxAge; int BounceCount;
	 }
*/

init_particles(X,X).
init_particles(P,Max) :-
	assert(particle(P,
			_, _, _,
			_, _, _,
			0.0, (-1.0), 0.0,
			_, _, _,
			0, _, _, _)),
	P1 is P + 1,
	init_particles(P1, Max).


activate_particles(X,X).
activate_particles(P,Max) :-
	rainbow(RAINBOW),
	particle(P,
		 _, _, _,
		 _, _, _,
		 _, _, _,
		 _, _, _,
		 Active, _, _, _),
	(   Active = 0
	->  random(0.0, 100.0, SX_Temp),
	    random(0.0, 100.0, SY_Temp),
	    random(0.0, 100.0, SZ_Temp),
	    SX is (((SX_Temp + 1.0) / 1000.0) - 0.05),
	    SY is ((SY_Temp + 50.0) / 500.0),
	    SZ is (((SZ_Temp + 1.0) / 1000.0) - 0.05),
	    max_particles_age(MPA),
	    (	RAINBOW = 0
	    ->	r(R),
		g(G),
		b(B),
		cR(CR),
		cG(CG),
		cB(CB),
		R1 is R + CR,
		G1 is G + CG,
		B1 is B + CB
	    ;	TEMP is integer((P + 1) / (Max / 11)),
		colors(TEMP, R1, G1, B1)
	    ),
	    retract(particle(P,
			     _, _, _,
			     _, _, _,
			     _, _, _,
			     _, _, _,
			     _, _, _, _)),
	    assert(particle(P,
			    0.0, 0.0, 0.0,
			    SX, SY, SZ,
			    0.0, (-1.0), 0.0,
			    R1, G1, B1,
			    1, 0, MPA, 0))
	;   write('')
	),
	P1 is P + 1,
	activate_particles(P1,Max).


adjust_particles(X,X).
adjust_particles(P,Max) :-
	xstrength(XSTRENGTH),
	ystrength(YSTRENGTH),
	zstrength(ZSTRENGTH),
	max_bounce_count(MBC),
	max_particles_age(MPA),
	particle(P,
		 X, Y, Z,
		 SX, SY, SZ,
		 TX, TY, TZ,
		 R, G, B,
		 Active, Age, MPA, Bounce),

	SX1 is (SX + ((TX - SX) / XSTRENGTH)),
	SY1 is (SY + ((TY - SY) / YSTRENGTH)),
	SZ1 is (SZ + ((TZ - SZ) / ZSTRENGTH)),
	X1 is (X + SX1),
	Y1 is (Y + SY1),
	Z1 is (Z + SZ1),
	retract(particle(P,
			 _, _, _,
			 _, _, _,
			 _, _, _,
			 _, _, _,
			 _, _, _, _)),
	assert(particle(P,
			X1, Y1, Z1,
			SX1,SY1, SZ1,
			TX, TY, TZ,
			R, G, B,
			Active, Age, MPA, Bounce)),

	(   Y1 < 0.0
	->  particle(P,
		     X2, _, Z2,
		     SX2,SY2_Temp, SZ2,
		     TX2, TY2, TZ2,
		     R2, G2, B2,
		     Active2, Age2, MPA2, Bounce2_Temp),
	    Y2 is 0.0,
	    SY2 is (-SY2_Temp),
	    Bounce2 is Bounce2_Temp + 1,
	    retract(particle(P,
			     _, _, _,
			     _, _, _,
			     _, _, _,
			     _, _, _,
			     _, _, _, _)),


	    (	Bounce2 > MBC
	    ->	assert(particle(P,
			    X2, Y2, Z2,
			    SX2,SY2, SZ2,
			    TX2, TY2, TZ2,
			    R2, G2, B2,
			    0, Age2, MPA2, Bounce2))
	    ;	assert(particle(P,
			    X2, Y2, Z2,
			    SX2,SY2, SZ2,
			    TX2, TY2, TZ2,
			    R2, G2, B2,
			    Active2, Age2, MPA2, Bounce2))
	    )
	;   write('')
	),
	Age_Temp is Age + 1,
	(   Age_Temp > MPA
	->  particle(P,
		     X3, Y3, Z3,
		     SX3,SY3, SZ3,
		     TX3, TY3, TZ3,
		     R3, G3, B3,
		     _, Age3, MPA3, Bounce3),
	    retract(particle(P,
			     _, _, _,
			     _, _, _,
			     _, _, _,
			     _, _, _,
			     _, _, _, _)),
	    assert(particle(P,
			    X3, Y3, Z3,
			    SX3, SY3, SZ3,
			    TX3, TY3, TZ3,
			    R3, G3, B3,
			    0, Age3, MPA3, Bounce3))
	;   write('')
	),
	P1 is P + 1,
	adjust_particles(P1,Max).





render_particles(X,X).
render_particles(P,Max) :-
	kGL_DEPTH_TEST(DEPTH),
	kGL_BLEND(BLEND),
	kGL_SRC_COLOR(SRC_COLOR),
	kGL_ONE(ONE),
	kGL_TEXTURE_2D(TEXTURE_2D),

	texture(1, BMP),
	glEnable(TEXTURE_2D),
	glBindTexture(TEXTURE_2D, BMP),

	glDisable(DEPTH),

	glEnable(BLEND),
	glBlendFunc(SRC_COLOR, ONE),

	particle(P,
		 X, Y, Z,
		 _, _, _,
		 _, _, _,
		 R, G, B,
		 _, _, _, _),
	glColor4f(R, G, B, 0.9),

	glPushMatrix,
	glTranslatef(X,Y,Z),

	kGL_QUADS(QUADS),
	glBegin(QUADS),
	glNormal3f(0.0, 0.0, 1.0),

	glTexCoord2f(0.0, 0.0),
	glVertex3f(-0.1, -0.1, 0.0),

	glTexCoord2f(1.0, 0.0),
	glVertex3f(0.1, -0.1, 0.0),

	glTexCoord2f(1.0, 1.0),
	glVertex3f(0.1, 0.1, 0.0),

	glTexCoord2f(0.0, 1.0),
	glVertex3f(-0.1, 0.1, 0.0),
	glEnd,

	glPopMatrix,

	glEnable(DEPTH),
	P1 is P + 1,
	render_particles(P1,Max).


display:-
	show_particles(SHOW),
	zoom(ZOOM),
	activate_particles(0,SHOW),
	adjust_particles(0,SHOW),
	% defs
	kGL_COLOR_BUFFER_BIT(COLOR_BUFFER),
	kGL_DEPTH_BUFFER_BIT(DEPTH_BUFFER),
	kGL_QUADS(QUADS),
	kGL_BLEND(BLEND),
	kGL_ONE_MINUS_SRC_ALPHA(ONE_MINUS),
	kGL_SRC_ALPHA(ALPHA),
	kGL_TEXTURE_2D(TEXTURE_2D),

	texture(2, BMP),
	% gl code
	glClear(COLOR_BUFFER \/ DEPTH_BUFFER),
	glLoadIdentity,

	glTranslatef(0.0, -1.0, ZOOM),

	% Reflection code
	glPushMatrix,
	glScalef(1.0, -1.0, 1.0),
	render_particles(0,SHOW),
	glPopMatrix,

	glEnable(BLEND),
	glBlendFunc(ONE_MINUS, ALPHA),

	glColor4f(0.0, 0.2, 0.2, 0.4),

	glEnable(TEXTURE_2D),
	glBindTexture(TEXTURE_2D, BMP),
	glBegin(QUADS),
	glNormal3f(0.0, 1.0, 0.0),
	glTexCoord2f(0.0, 0.0), glVertex3f(-10.0, 0.0, 10.0),
	glTexCoord2f(1.0, 0.0), glVertex3f( 10.0, 0.0, 10.0),
	glTexCoord2f(1.0, 1.0), glVertex3f( 10.0, 0.0,-10.0),
	glTexCoord2f(0.0, 1.0), glVertex3f(-10.0, 0.0,-10.0),
	glEnd,


	render_particles(0,SHOW),
	sleep(20),
	glutSwapBuffers.


load_texture(X) :-
	bmp(X, BMP),
	kGL_TEXTURE_2D(TEXTURE_2D),
	kGL_TEXTURE_MAG_FILTER(MAG_FILTER),
	kGL_TEXTURE_MIN_FILTER(MIN_FILTER),
	kGL_RGB(RGB),
	kGL_UNSIGNED_BYTE(UNSIGNED_BYTE),
	kGL_LINEAR(LINEAR),
	loadGLTextures(BMP, Width, Height, Data),
	glGenTextures(1, TextureNames),
	nth1(1, TextureNames, Texture),
	glBindTexture(TEXTURE_2D, Texture),
	glTexParameteri(TEXTURE_2D, MAG_FILTER, LINEAR),
	glTexParameteri(TEXTURE_2D, MIN_FILTER, LINEAR),
	glTexImage2D(TEXTURE_2D, 0, 3, Width, Height, 0, RGB, UNSIGNED_BYTE, Data),
	assert(texture(X, Texture)),
	write('Texture: '),write(Texture),nl.


init:-
	% defs
	kGL_SMOOTH(SMOOTH),
	kGL_PERSPECTIVE_CORRECTION_HINT(PERSPECTIVE),
	kGL_POINT_SMOOTH_HINT(SMOOTH_HINT),
	kGL_NICEST(NICEST),
	kGL_DEPTH_TEST(DEPTH_TEST),
	% gl code
	glShadeModel(SMOOTH),
	glClearColor(0.0, 0.0, 0.0, 0.5),
	glClearDepth(1.0),
	glDisable(DEPTH_TEST),
	glHint(PERSPECTIVE,NICEST),
	glHint(SMOOTH_HINT,NICEST),
	load_texture(1),
	load_texture(2),
	max_particles(Max),
	init_particles(0, Max).


reshape:-
	% defs
	fullscreen(F),
	X is 0,
	Y is 0,
	width(W),
	height(H),
	kGLUT_SCREEN_WIDTH(SCREEN_WIDTH),
	kGLUT_SCREEN_HEIGHT(SCREEN_HEIGHT),
	glutGet(SCREEN_WIDTH,SW),
	glutGet(SCREEN_HEIGHT,SH),
	%Aspect is float(W) / float(H),
	kGL_PROJECTION(PROJECTION),
	kGL_MODELVIEW(MODELVIEW),
	% gl code
	(   F = 1
	->  glViewport(X,Y,SW,SH)
	;   glViewport(X,Y,W,H)
	),
	glMatrixMode(PROJECTION),
	glLoadIdentity,
	(   F = 1
	->  AF is float(SW) / float(SH),
	    gluPerspective(45.0, AF, 0.1, 100.0)
	;   ANF is float(W) / float(H),
	    gluPerspective(45.0, ANF, 0.1, 100.0)
	),
	glMatrixMode(MODELVIEW),
	glLoadIdentity.


keyboard(27,_,_) :-
	write('Closing Window and Exiting...'),nl,
	glutDestroyWindow.
% Press 1 key
keyboard(49,_,_) :-
	xstrength(X),
	(   X > 1.0
	->  X1 is X - 1.0,
	    retract(xstrength(_)),
	    assert(xstrength(X1)),
	    write('X Strength: '),write(X1),nl
	;   write('X Strength: '),write(X),nl
	).
% Press 2 key
keyboard(50,_,_) :-
	xstrength(X),
	(   X < 100.0
	->  X1 is X + 1.0,
	    retract(xstrength(_)),
	    assert(xstrength(X1)),
	    write('X Strength: '),write(X1),nl
	;   write('X Strength: '),write(X),nl
	).
% Press 3 key
keyboard(51,_,_) :-
	ystrength(X),
	(   X > 1.0
	->  X1 is X - 1.0,
	    retract(ystrength(_)),
	    assert(ystrength(X1)),
	    write('Y Strength: '),write(X1),nl
	;   write('Y Strength: '),write(X),nl
	).
% Press 4 key
keyboard(52,_,_) :-
	ystrength(X),
	(   X < 100.0
	->  X1 is X + 1.0,
	    retract(ystrength(_)),
	    assert(ystrength(X1)),
	    write('Y Strength: '),write(X1),nl
	;   write('Y Strength: '),write(X),nl
	).
% Press 5 key
keyboard(53,_,_) :-
	zstrength(X),
	(   X > 1.0
	->  X1 is X - 1.0,
	    retract(zstrength(_)),
	    assert(zstrength(X1)),
	    write('Z Strength: '),write(X1),nl
	;   write('Z Strength: '),write(X),nl
	).
% Press 6 key
keyboard(54,_,_) :-
	zstrength(X),
	(   X < 100.0
	->  X1 is X + 1.0,
	    retract(zstrength(_)),
	    assert(zstrength(X1)),
	    write('Z Strength: '),write(X1),nl
	;   write('Z Strength: '),write(X),nl
	).

% Press 7 key
keyboard(55,_,_) :-
	show_particles(X),
	(   X > 10
	->  X1 is X - 10,
	    retract(show_particles(_)),
	    assert(show_particles(X1)),
	    write('Particles: '),write(X1),nl
	;   write('Particles: '),write(X),nl
	).
% Press 7 key
keyboard(56,_,_) :-
	show_particles(X),
	(   X < 1000
	->  X2 is X + 10,
	    retract(show_particles(_)),
	    assert(show_particles(X2)),
	    write('Particles: '),write(X2),nl
	;   write('Particles: '),write(X),nl
	).
% Press r key
keyboard(114,_,_) :-
	rainbow(X),
	(   X = 0
	->  retract(rainbow(_)),
	    assert(rainbow(1)),
	    write('Rainbow On'),nl
	;   retract(rainbow(_)),
	    assert(rainbow(0)),
	    write('Rainbow Off'),nl
	).
% Press w key
keyboard(119,_,_) :-
	zoom(X),
	(   X < -4.0
	->  X1 is X + 1.0,
	    retract(zoom(_)),
	    assert(zoom(X1)),
	    write('Zoom: '),write(X1),nl
	;   write('Zoom: '),write(X),nl
	).
% Press s key
keyboard(115,_,_) :-
	zoom(X),
	(   X > -10.0
	->  X1 is X - 1.0,
	    retract(zoom(_)),
	    assert(zoom(X1)),
	    write('Zoom: '),write(X1),nl
	;   write('Zoom: '),write(X),nl
	).
% Press f to toggle fullscreen
keyboard(102,_,_) :-
	width(W),
	height(H),
	fullscreen(F),
	(   F = 0
	->  glutFullScreen,
	    retract(fullscreen(_)),
	    assert(fullscreen(1))
	;   glutReshapeWindow(W,H),
	    retract(fullscreen(_)),
	    assert(fullscreen(0))
	).


idle:-
	display.


main:-
        % defs
	width(W),
	height(H),
	kGLUT_DOUBLE(DOUBLE),
	kGLUT_RGB(RGB),
	kGLUT_ALPHA(ALPHA),
	% gl code
	glutInit,
	glutInitDisplayMode(DOUBLE \/ RGB \/ ALPHA),
	glutInitWindowSize(W, H),
	glutInitWindowPosition(0,0),
	glutCreateWindow('Particle Engine'),
	init,
	glutDisplayFunc,
	glutIdleFunc(idle),
	glutReshapeFunc,
	glutKeyboardFunc,
	glutMainLoop.



