
with glfw3; use glfw3;
with zoomwheel; use zoomwheel;
with gametypes; use gametypes;

with gl; use gl;
with gl.binding; use gl.binding;
with system; use system;

with matutils; use matutils;
with interfaces.c; use interfaces.c;
with interfaces.c.strings; use interfaces.c.strings;
with text_io; use text_io;


package body gameutils is




dragging: boolean := false;
oldTimeMs : float := 0.0; --float(glfwGetTime);

guxold, guyold : gldouble := 0.0;




procedure handle_mouse(
	pick:boolean; --flag to treat space-Key as a mouseBtn click
	imin, imax : integer; -- bounds for isel
	isel: in out integer; -- picked-object index
	centroid: gametypes.giparray; -- object-centers
	xcc,ycc,zcc: float -- puzzle 3D radii = geometric-centers
) is

	mdlay: constant float := 0.2;
	guxpos,guypos : aliased gldouble;
	msl,msr : glint;

	dt, nowTime: float;









	procedure selectRight is
		fmx,fmy: float;
		fmz : float := -float(zoomwheel.zdist);
		mindist : float := 1000.0;
		xc,yc,zc, xndc, yndc, zndc, xn,yn,zn, dist : float;
		ix,iy,iz : integer;
		v4, vcc : vec4;
		found : boolean := false;
	begin

		fmx:=float(guxpos)/float(gametypes.wwid);
		fmy:=1.0 - float(guypos)/float(gametypes.whit);

		mindist:=1000.0;
		for kk in imin..imax loop
			ix:=centroid(kk)(1); -- 1..3
			iy:=centroid(kk)(2); -- 1..5
			iz:=centroid(kk)(3); -- 1..1

			xc:= float(ix)-xcc; -- -1 ... +1
			yc:= float(iy)-ycc; -- -2 ... +2
			zc:= float(iz)-zcc; -- -0 ... +0

			v4:=(xc,yc,zc,1.0); 
			matXvec( mvp, v4, vcc );

			xndc:=vcc(1)/vcc(4); --// ndc in [-1..1]
			yndc:=vcc(2)/vcc(4); --// 
			zndc:=vcc(3)/vcc(4); --// bigger zndc if further away

			xn:=(1.0+xndc)/2.0;
			yn:=(1.0+yndc)/2.0;
			zn:=(1.0+zndc)/2.0;


			dist := (fmx-xn)*(fmx-xn)+(fmy-yn)*(fmy-yn)+(fmz-zn)*(fmz-zn);
			if( dist < mindist ) then
				found:=true;
				mindist:=dist;
				isel:=kk;
			end if;

		end loop; --kk

--if found then
--put_line("Selected # "&integer'image(isel));
--else
--put_line("selection NOT found");
--end if;

	end selectRight;







begin


	glfwgetcursorpos(mainWin,guxpos'access,guypos'access);
	msr := glfwGetMouseButton(mainWin, glfw_mouse_button_2);
	msl := glfwGetMouseButton(mainWin, glfw_mouse_button_1);

	if msr=glfw_press or pick then

		nowTime:=float(glfwGetTime);
		dt:=nowTime-oldTimeMs;
		if dt>mdlay then
			oldTimeMs:=nowTime;
			selectRight;
		end if;

	elsif msl=glfw_press then

		if dragging then
			roty:=0.2*float(guxpos-guxold);
			rotx:=0.2*float(guypos-guyold);
			guxold:=guxpos;
			guyold:=guypos;
		else --not dragging yet
			dragging:=true;
			guxold:=guxpos;
			guyold:=guypos;
		end if;

	elsif msl=glfw_release then
		dragging:=false;
	end if;

end handle_mouse;





procedure getKeyInputs( 
	mainWin : access GLFWwindow; 
	imin, imax : in integer;
	isel: in out integer;
	msel: out boolean ) is

	osel: constant integer := isel;

	keydlay: constant float := 0.2;
	nowTime, deltaTime: float;

	zmin : constant gldouble := zoomwheel.zmin;
	zmax : constant gldouble := zoomwheel.zmax;

	--this delays too much and is annoying:
	procedure xflushkey( scancode: int ) is
	begin
		while glfwgetkey( mainWin, scancode ) /= Glfw_Release loop
			GlfwPollEvents;
		end loop;
	end xflushkey;

	procedure flushkey( scancode: int ) is
	begin
		null;
	end flushkey;

begin

	msel:=false;

	nowTime := float(glfwGetTime);
	deltaTime := nowTime - oldTimeKb;



-- First, we process continuous key-press responses:-----------------

if glfwgetkey( mainWin, glfw_key_escape ) = Glfw_Press then
		userexit:=true;
		return;

elsif glfwgetkey( mainWin, glfw_key_q ) = Glfw_Press then
		userexit:=true;
		return;

--Nearer
elsif glfwgetkey( mainWin, glfw_key_i ) = Glfw_Press then -- zoom-In
			zoomwheel.zdist:=0.99*zoomwheel.zdist;
			if zoomwheel.zdist<zmin then zoomwheel.zdist:=zmin; end if;

--Away
elsif glfwgetkey( mainWin, glfw_key_o ) = Glfw_Press then -- zoom-Out
			zoomwheel.zdist:=1.01*zoomwheel.zdist;
			if zoomwheel.zdist>zmax then zoomwheel.zdist:=zmax; end if;

--end if; 24dec21



-- Second, we process discrete key-press events -----------------------

elsif deltaTime > keydlay then 


	nowTime := float(glfwGetTime);


	-- note key assignments for tombs 2,4,5,6:
	--subtype rngp2 is integer range 1..5;  s->5,
	--subtype rngp4 is integer range 0..10; s->10
	--subtype rngp5 is integer range 1..9;  s->9,
	--subtype rngp6 is integer range 1..9;  s->9, m->9,


	-- 8oct21 addition to toggle Numerals:
	if glfwgetkey( mainWin, glfw_key_n ) = Glfw_Press then
		flushkey(glfw_key_n);
		numerals := not numerals;
		oldTimeKb := nowTime;


	-- Zero must be guarded:
	elsif glfwgetkey( mainWin, glfw_key_0 ) = Glfw_Press then
		flushkey(glfw_key_0);
		if imin<=0 then --zero is valid
			isel:=0;
		end if;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_1 ) = Glfw_Press then
		flushkey(glfw_key_1);
		isel:=1;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_2 ) = Glfw_Press then
		flushkey(glfw_key_2);
		isel:=2;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_3 ) = Glfw_Press then
		flushkey(glfw_key_3);
		isel:=3;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_4 ) = Glfw_Press then
		flushkey(glfw_key_4);
		isel:=4;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_5 ) = Glfw_Press then
		flushkey(glfw_key_5);
		isel:=5;
		oldTimeKb := nowTime;


-- higher numbers (6..9) must be guarded: ---------------------------

	elsif glfwgetkey( mainWin, glfw_key_6 ) = Glfw_Press then
		flushkey(glfw_key_6);
		if imax>=6 then
			isel:=6;
		end if;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_7 ) = Glfw_Press then
		flushkey(glfw_key_7);
		if imax>=7 then
			isel:=7;
		end if;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_8 ) = Glfw_Press then
		flushkey(glfw_key_8);
		if imax>=8 then
			isel:=8;
		end if;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_9 ) = Glfw_Press then
		flushkey(glfw_key_9);
		if imax>=9 then
			isel:=9;
		end if;
		oldTimeKb := nowTime;








	-- s=>Skull
	elsif glfwgetkey( mainWin, glfw_key_s ) = Glfw_Press then
		flushkey(glfw_key_s);
		isel:=imax;
		oldTimeKb := nowTime;

	-- m=>Medusa
	elsif glfwgetkey( mainWin, glfw_key_m ) = Glfw_Press then
		flushkey(glfw_key_s);
		isel:=imax;
		oldTimeKb := nowTime;




	elsif glfwgetkey( mainWin, glfw_key_c ) = Glfw_Press then
		flushkey(glfw_key_c);
		tryNextSkin:=true;
		oldTimeKb := nowTime;






	elsif glfwgetkey( mainWin, glfw_key_up ) = Glfw_Press then
		flushkey(glfw_key_up);
		goUp:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_down ) = Glfw_Press then
		flushkey(glfw_key_down);
		goDown:=true;
		oldTimeKb := nowTime;



	elsif glfwgetkey( mainWin, glfw_key_left ) = Glfw_Press then
		flushkey(glfw_key_left);
		goLeft:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_right ) = Glfw_Press then
		flushkey(glfw_key_right);
		goRight:=true;
		oldTimeKb := nowTime;



	elsif glfwgetkey( mainWin, glfw_key_right_shift ) = Glfw_Press then
		flushkey(glfw_key_right_shift);
		goAway:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_left_shift ) = Glfw_Press then
		flushkey(glfw_key_left_shift);
		goNear:=true;
		oldTimeKb := nowTime;














	elsif glfwgetkey( mainWin, glfw_key_u ) = Glfw_Press then
		flushkey(glfw_key_u);
		goUp:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_d ) = Glfw_Press then
		flushkey(glfw_key_d);
		goDown:=true;
		oldTimeKb := nowTime;



	elsif glfwgetkey( mainWin, glfw_key_l ) = Glfw_Press then
		flushkey(glfw_key_l);
		goLeft:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_r ) = Glfw_Press then
		flushkey(glfw_key_r);
		goRight:=true;
		oldTimeKb := nowTime;



	elsif glfwgetkey( mainWin, glfw_key_b ) = Glfw_Press then
		flushkey(glfw_key_b);
		goAway:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_f ) = Glfw_Press then
		flushkey(glfw_key_f);
		goNear:=true;
		oldTimeKb := nowTime;










	elsif glfwgetkey( mainWin, glfw_key_enter ) = Glfw_Press then
		flushkey(glfw_key_enter);
		msel:=true;
		oldTimeKb := nowTime;

	elsif glfwgetkey( mainWin, glfw_key_h ) = Glfw_Press then
		flushkey(glfw_key_h);
		help:=not help;

		if help then
			zsave:=zoomwheel.zdist;
			zoomwheel.zdist := zmax;
		else
			zoomwheel.zdist := zsave;
		end if;

		oldTimeKb := nowTime;


	elsif glfwgetkey( mainWin, glfw_key_v ) = Glfw_Press then -- v=volume (on/off)
		flushkey(glfw_key_v);
		mute := not mute;
		oldTimeKb := nowTime;


	elsif glfwgetkey( mainWin, glfw_key_a ) = Glfw_Press then
		flushkey(glfw_key_a);
		show_axes := not show_axes;
		oldTimeKb := nowTime;


	--restart
	elsif glfwgetkey( mainWin, glfw_key_space ) = Glfw_Press then
		flushkey(glfw_key_space);
		doRestart:=true;
		oldTimeKb := nowTime;

	end if;

	--debug only
	if isel/=osel then
		--put_line("choosing isel="&integer'image(isel));
		null;
	end if;

end if;

end getKeyInputs;





procedure InitGLFW( 
	wid, hit : out glint; 
	fwd,fht : out glint; 
	name: string ) is

	use system;

	title : interfaces.c.strings.chars_ptr := new_string(name&ascii.nul);

	maj,min,rev : aliased glint;

	axs, ays : aliased float;
	awwid,awhit, afwid, afhit : aliased glint;

begin

	put_line("...using fastrgv's Ada Binding to GLFW...");

	GlfwGetVersion(maj'access,min'access,rev'access); --naturals
	put("GLFW ver: ");
	put(glint'image(maj));
	put(":"&glint'image(min));
	put(":"&glint'image(rev));
	New_Line;



	if GlfwInit /= gl_true then
		new_line;
		put_line("glfwInit failed");
		raise program_error;
	end if;

	-- use version here that your graphics card would support:
	GlfwWindowHint( glfw_context_version_major, 3);
	GlfwWindowHint( glfw_context_version_minor, 3);
	GlfwWindowHint( glfw_opengl_forward_compat, gl_true);
	GlfwWindowHint( glfw_opengl_profile, glfw_opengl_core_profile);

	GlfwWindowHint( glfw_samples, 4);
	GlfwWindowHint( glfw_client_api, glfw_opengl_api);

	-- this seems unnecessary...
	-- MacBook shows this app @ HiDpi by default!
	--GlfwWindowHint( glfw_cocoa_retina_framebuffer, glfw_true );


	wid:=800;
	hit:=800;

	mainWin := glfwcreatewindow(
		wid, hit,	title, 
		null, null );
		

	if mainWin = null then
		new_line;
		put_line("glfwCreateWindow failed");
		raise program_error;
	end if;

	glfwmakecontextcurrent( mainWin );


--HiDpi queries:
	glfwGetWindowSize(mainWin, awwid'access, awhit'access);
	glfwGetFramebufferSize(mainWin, afwid'access,afhit'access);
	glfwGetWindowContentScale(mainWin, axs'access,ays'access);

	wid:=awwid;
	hit:=awhit;

	fwd:=afwid;
	fht:=afhit;


	put_line("HighDpi Queries:");
	put_line("WI: "&glint'image(awwid)&","&glint'image(awhit));
	put_line("FB: "&glint'image(afwid)&","&glint'image(afhit));
	put_line("Sc: "&float'image(axs)&","&float'image(ays));

end InitGLFW;









	procedure updateMVP( wid,hit : float) is
		zeye : float := float(zoomwheel.zdist);
	begin

		perspective(pm, 45.0, wid/hit,  0.1, 100.0);

		--            eye             cen           up
		lookat(vm, 0.0,0.0,zeye,  0.0,0.0,0.0,  0.0,1.0,0.0 );

		degRevRotate(mm, rotx, roty, rotz ); --updates mm
		rotx:=0.0; roty:=0.0; rotz:=0.0;

		mvp:=mm;
		matXmat(mvp,vm);
		matXmat(mvp,pm);

	end updateMVP;













procedure myassert( condition : boolean;  flag: integer:=0 ) is
begin
  if condition=false then
  		put("ASSERTION Failed!  ");
		if flag /= 0 then
			put_line( "@ " & integer'image(flag) );
		end if;
		new_line;
  		raise program_error;
  end if;
end myassert;



--used for debugging:
function dumpGLerrorQueue(id: string) return integer is
	use gl.binding;
	errno: interfaces.c.unsigned;
	isAnError: boolean;
	ercount: integer := 0;
begin
	isAnError:=false;
	loop
		errno:=glGetError;
		exit when errno=gl_no_error;
		ercount:=ercount+1;
		isAnError:=true;
		put("GLerror=");
		put(interfaces.c.unsigned'image(errno));
		new_line;
	end loop;
	if isAnError then
		put_line("...@ id="&id);
	end if;
	return ercount;
end dumpGLerrorQueue;
--
-- 16#0#   =    0 = no_error
-- 16#500# = 1280 = invalid_enum
-- 16#501# = 1281 = invalid_value
-- 16#502# = 1282 = invalid_operation ?reusing uniformID?
-- 16#503# = 1283 = stack_overflow
-- 16#504# = 1284 = stack_underflow
-- 16#505# = 1285 = out_of_memory
--









end gameutils;

