

--
-- Copyright (C) 2022  <fastrgv@gmail.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You may read the full text of the GNU General Public License
-- at <http://www.gnu.org/licenses/>.
--


-- 4tomb.adb ( 4x4x4 )
-- 19jan16: changing corners 7..8 to be simple, 
-- vertical legs : 1x2x1
-- to a table-shaped enclosure for the skull
-- to see if the result is a) solvable, and b) challenging.
--
-- Note:  had to change centroids of 7,8 to reference actual pos
--


with snd4ada;

with gl, gl.binding, gl.pointers;
with glu, glu.binding, glu.pointers;
with glext, glext.binding, glext.pointers;

-------------------------------------------------------------
with System;
with Interfaces.C;
use  type interfaces.c.unsigned;
with Interfaces.C.Pointers;
with interfaces.c.strings;


----------------------------------------------------------------

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

----------------------------------------------------------------

with matutils;
with stex;

with ada.numerics.generic_elementary_functions;

with Ada.Strings.Fixed;
with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;

with unchecked_deallocation;

with text_io;
with pngloader;
with gametypes;
with matutils;

with pictobj;
with pictobj2;

with shader;  use shader;



with Ada.Command_Line;
with Ada.Directories;
with SysUtils;

with gameutils; use gameutils;

----------------------------------------------------------------



procedure tomb2 is

	dirname: constant string := ada.directories.current_directory;
	onMac : boolean := (ada.strings.fixed.index( dirname, "MacOS", 1 ) > 1);
	onGnu : boolean := (ada.strings.fixed.index( dirname, "gnu", 1 ) > 1);
	onGnat : boolean := (ada.strings.fixed.index( dirname, "gnat", 1 ) > 1);
	onLinux : constant boolean := onGnu or onGnat;

------- begin game specific code -------------------

	mx : constant integer := 4;
	mz : constant integer := 2;
	my : constant integer := 6;

	ncubes  : constant integer := mx*my*mz;

	subtype rngm is integer range 1..ncubes;


	empty : array(1..mx,1..my) of boolean;



	-- overall object drawing centroid (move to origin):
	xxcc : constant float := float(mx)/2.0;
	yycc : constant float := float(my)/2.0;
	zzcc : constant float := float(mz)/2.0;

------- end game specific code -------------------



use text_io;
use gametypes;
use pngloader;
use matutils;
use gametypes.fmath;

use interfaces.c;
use interfaces.c.strings;
use glext;
use glext.pointers;
use glext.binding;
use gl;
use gl.binding;
use gl.pointers;


	origskin : boolean := true; -- false => Vadasz colors





procedure initEmptyHi is -- skull @ top
begin

	for ix in 1..mx loop
			for iy in 1..my loop
				empty(ix,iy) := false;
			end loop;
	end loop;


	-- top left/right face:
	for iy in 5..6 loop
		empty(1,iy):=true;
		empty(4,iy):=true;
	end loop;

	-- empty center
	for ix in 2..3 loop
	for iy in 2..3 loop
		empty(ix,iy):=true;
	end loop;
	end loop;


end initEmptyHi;










procedure initEmptyMidLo is -- skull @ center, space @ bottom
begin

	for ix in 1..mx loop
			for iy in 1..my loop
				empty(ix,iy) := false;
			end loop;
	end loop;

	-- bottom 2 layers
	for ix in 1..4 loop
	for iy in 1..2 loop
		empty(ix,iy):=true;
	end loop;
	end loop;

end initEmptyMidLo;



procedure initEmptyMidHi is -- skull @ center, space @ top
begin

	for ix in 1..mx loop
			for iy in 1..my loop
				empty(ix,iy) := false;
			end loop;
	end loop;

	-- bottom 2 layers
	for ix in 1..4 loop
	for iy in 5..6 loop
		empty(ix,iy):=true;
	end loop;
	end loop;

end initEmptyMidHi;



procedure initEmptyMidMid is -- skull @ center, space @ top&bottom
begin

	for ix in 1..mx loop
			for iy in 1..my loop
				empty(ix,iy) := false;
			end loop;
	end loop;

	-- bottom 2 layers
	for ix in 1..4 loop
		empty(ix,6):=true;
		empty(ix,1):=true;
	end loop;

end initEmptyMidMid;


























-- define 9 puzzle pieces here, and initialize:
mp : constant integer := 5;
subtype rngp is integer range 1..mp;
subtype rngp0 is integer range 0..mp;
subtype fparray is gfparray(rngp);
subtype iparray is giparray(rngp);


movedelay : constant float := 0.25; -- seconds to move a block
movebegin : float := 0.0;
oxc,oyc,ozc : integer;
grinding : array(rngp) of boolean := ( others=>false );

anyGrinding: boolean := false;


num_texid : array(rngp0) of gluint;
box_texid : gluint;



-- normal starting config high skull
centroidHi : constant iparray :=
-- piece-centroids initialization:
( 
		1=>(1,1,1),
		2=>(3,1,1),
		3=>(1,3,1),
		4=>(3,3,1),

		5=>(2,5,1)  --lower left corner of skull
);


-- starting config low skull
centroidLo : constant iparray :=
-- piece-centroids initialization:
( 
		1=>(1,3,1),
		2=>(3,3,1),
		3=>(1,5,1),
		4=>(3,5,1),

		5=>(2,1,1)  --lower left corner of skull
);


-- goal with low space
centroidMidLo : constant iparray :=
-- piece-centroids initialization:
( 
		1=>(1,3,1), --lra
		2=>(3,3,1), --lla
		3=>(1,5,1), --lrp
		4=>(3,5,1), --llp

		5=>(2,4,1)  --to be entombed in center
);



-- goal with high space
centroidMidHi : constant iparray :=
-- piece-centroids initialization:
( 
		1=>(1,1,1), --lra
		2=>(3,1,1), --lla
		3=>(1,3,1), --lrp
		4=>(3,3,1), --llp

		5=>(2,2,1)  --to be entombed in center
);






-- goal with split space
centroidMidMid : constant iparray :=
-- piece-centroids initialization:
( 
		1=>(1,2,1), --lra
		2=>(3,2,1), --lla
		3=>(1,4,1), --lrp
		4=>(3,4,1), --llp

		5=>(2,3,1)  --to be entombed in center
);











--Note that for each piece, its full extent is:
-- (p.x,p.y,p.z)...(p.x+1,p.y+1,p.z+1)

centroid : iparray;


--offsets from piece-centroid to space-centroid:
space : constant iparray :=
( 
		1=>(1,1,0), --ll
		2=>(0,1,0), --lr
		3=>(1,0,0), --ul
		4=>(0,0,0), --ur

		5=>(0,0,0)  --unused
);






normalMode : boolean := true;
windelay : constant float := 1.0;
testime : float := 0.0;


procedure test4winner is
begin

	winner := 
		(centroid=centroidMidHi) or
		(centroid=centroidMidMid) or
		(centroid=centroidMidLo);

	if winner then
		testime := float(glfwGetTime);
	end if;

end test4winner;





mxskin: constant integer := 4;
skin: integer := mxskin-1;

procedure setNextSkin is
	macpath: string := "../../../data/"; -- osx
	xinpath: string := "../../data/"; -- windows or linux
	nopath: string := "./data/"; --below current dir
	path: unbounded_string;
begin

	if onmac then
		append(path,macpath);
	elsif ada.directories.exists(nopath) then
		append(path,nopath);
	else
		append(path,xinpath);
	end if;



	skin := (1+skin) mod mxskin; --0..3
	myassert( (skin < mxskin) and (skin>=0) );

	if skin=0 then --Original colors

		num_texid(0) := loadPng(mirror,to_string(path)&"none.png");
		num_texid(1) := loadPng(mirror,to_string(path)&"one.png");
		num_texid(2) := loadPng(mirror,to_string(path)&"two.png");
		num_texid(3) := loadPng(mirror,to_string(path)&"three.png");
		num_texid(4) := loadPng(mirror,to_string(path)&"four.png");

	elsif skin=1 then --wornRock

		num_texid(0) := loadPng(mirror,to_string(path)&"worn.png");
		num_texid(1) := loadPng(mirror,to_string(path)&"worn1.png");
		num_texid(2) := loadPng(mirror,to_string(path)&"worn2.png");
		num_texid(3) := loadPng(mirror,to_string(path)&"worn3.png");
		num_texid(4) := loadPng(mirror,to_string(path)&"worn4.png");

	elsif skin=2 then --wood

		num_texid(0) := loadPng(mirror,to_string(path)&"wood.png");
		num_texid(1) := loadPng(mirror,to_string(path)&"w1.png");
		num_texid(2) := loadPng(mirror,to_string(path)&"w2.png");
		num_texid(3) := loadPng(mirror,to_string(path)&"w3.png");
		num_texid(4) := loadPng(mirror,to_string(path)&"w4.png");

	elsif skin=3 then --granite

		num_texid(0) := loadPng(mirror,to_string(path)&"gran.png");
		num_texid(1) := loadPng(mirror,to_string(path)&"g1.png");
		num_texid(2) := loadPng(mirror,to_string(path)&"g2.png");
		num_texid(3) := loadPng(mirror,to_string(path)&"g3.png");
		num_texid(4) := loadPng(mirror,to_string(path)&"g4.png");

	end if;

end setNextSkin;



procedure first_prep is -- main program setup
	macpath: string := "../../../data/"; -- osx
	xinpath: string := "../../data/"; -- windows or linux
	nopath: string := "./data/"; --below current dir
	path: unbounded_string;
	fontcol: constant vec4 := (0.0,0.0,0.0,1.0); --black

	fontfile: constant string := "data/NotoSans-Regular.ttf";
	annex: constant string := "../../";
	nerr: integer;
begin

	if onmac then
		append(path,macpath);
	elsif ada.directories.exists(nopath) then
		append(path,nopath);
	else
		append(path,xinpath);
	end if;


	snd4ada.initSnds;

	brick := snd4ada.initSnd(
		Interfaces.C.Strings.New_String(to_string(path)&"concrete_third.wav"));
	
	whoosh := snd4ada.initSnd(
		Interfaces.C.Strings.New_String(to_string(path)&"whoosh_4th.wav"));
	
	fanfare := snd4ada.initSnd(
		Interfaces.C.Strings.New_String(to_string(path)&"fanfare.wav"));
	
	shriek := snd4ada.initSnd(
		Interfaces.C.Strings.New_String(to_string(path)&"medusa.wav"));
	
	clap := snd4ada.initSnd(
		Interfaces.C.Strings.New_String(to_string(path)&"applause.wav"));
	

	if brick<0 or whoosh<0 or fanfare<0 or shriek<0 or clap<0 then
		put_line("snd4ada.initSnds ERROR");
		raise program_error;
	end if;





------- begin GLFW prep ---------------------------------------------------

	gameutils.InitGlfw( wwid,whit,fwid,fhit, "Reliquary2 - CrystalSkull");
	zoomwheel.enable(mainWin);


	-- prepare font -------------
	if ada.directories.exists(fontfile) then
		stex.InitFont ( fontfile );
	else
		stex.InitFont ( annex&fontfile );
	end if;
	stex.setColor( fontcol );
	stex.reSize(wwid,whit);


	glViewport(0,0,Fwid,Fhit);


	glgenvertexarrays(1, vertexarrayid'address );
	glbindvertexarray(vertexarrayid);

	glactivetexture(gl_texture0); -- moved here 5nov14 (outside main loop)

	glgenbuffers(1, vertbuff'address);
	glgenbuffers(1, rgbbuff'address);
	glgenbuffers(1, uvbuff'address);
	glgenbuffers(1, elembuff'address);


	glenable(gl_depth_test);
	gldepthfunc( gl_lequal );
	glenable( gl_cull_face );


	-- theoretically reduces aliasing (can't tell for sure):
	glEnable(GL_MULTISAMPLE);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);

	--glClearColor(0.7,0.7,0.7,1.0);
	glClearColor(0.4,0.4,0.4,1.0);

	nerr:=dumpGLerrorQueue("main 1"); 
	--prevents misleading messages in pngloader or loadshaders



	box_texid := loadPng(mirror,to_string(path)&"fence.png");

	setNextSkin;

	num_texid(5) := loadPng(mirror,to_string(path)&"tskull.png");



	pgmTexShadID := loadshaders(to_string(path)&"texobj.vs",to_string(path)&"texobj.fs");
	matrixid := glgetuniformlocation(pgmtexshadid, pmvp);
	uniftex  := glgetuniformlocation(pgmtexshadid, pmyts);

	pgmTexID := loadshaders(to_string(path)&"otexobj.vs",to_string(path)&"otexobj.fs");
	matid := glgetuniformlocation(pgmtexid, pmvp);
	unitex  := glgetuniformlocation(pgmtexid, pmyts);


	-- note:  deep copies of structures are automatic in Ada
	--        (unless we use pointer assignments)
	--if normalMode then
	centroid:=centroidHi;
	initEmptyHi;
	--end if;

end first_prep;















-- the enclosing chainlink box:
procedure drawBigCube( po: pictobj2.pictangle ) is

	xr : constant float := 2.02;
	yr : constant float := 3.03;
	zr : constant float := 1.01;

	xc : constant float := xr - xxcc;
	yc : constant float := yr - yycc;
	zc : constant float := zr - zzcc;

	r,g,b : constant float := 0.5;
begin
	pictobj2.setrect( po, xc,yc,zc, xr,yr,zr, r,g,b);
	pictobj2.draw( po, vertbuff, uvbuff, elembuff );
end drawBigCube;











-- this is the 5th puzzle piece...double size: 2x2x1
procedure draw5( po: pictobj.pictangle; nowTime: float ) is
	tt, r,g,b, xc,yc,zc, xt,yt,zt, xo,yo,zo : float;
	ix,iy,iz: integer;
begin

	ix:=centroid(5)(1);
	iy:=centroid(5)(2);
	iz:=centroid(5)(3);
	xc:=float(ix)-xxcc;
	yc:=float(iy)-yycc;
	zc:=float(iz)-zzcc;

	xo:=float(oxc)-xxcc;
	yo:=float(oyc)-yycc;
	zo:=float(ozc)-zzcc;

	r:=0.6; g:=0.6; b:=0.6;

	if grinding(5) then
		tt := (nowTime-movebegin)/movedelay;
		if tt>1.0 then
			pictobj.setrect( po, xc,yc,zc, 0.95,0.95,0.95, r,g,b);
			grinding(5):=false;
		else
			xt := xo + tt*(xc-xo);
			yt := yo + tt*(yc-yo);
			zt := zo + tt*(zc-zo);
			pictobj.setrect( po, xt,yt,zt, 0.95,0.95,0.95, r,g,b);
		end if;
	else
		pictobj.setrect( po, xc,yc,zc, 0.95,0.95,0.95, r,g,b);
	end if;



	--pictobj.draw( po, vertbuff, rgbbuff, uvbuff, elembuff );
	pictobj.draw( po, vertbuff, uvbuff, elembuff );

end draw5;










-- these are individual 1x1 parts of each of corner pieces,
-- where the 8 corners have 7 cubelets each:
procedure drawCube( 
	po: pictobj.pictangle; ii, 
	cx,cy,cz, i,j,k, ixc,iyc,izc : integer; nowTime: float ) is

	xc : float := float(ixc+i) -0.5 - xxcc;
	yc : float := float(iyc+j) -0.5 - yycc;
	zc : float := float(izc+k) -0.5 - zzcc;

	xo : float := float(oxc+i) -0.5 - xxcc;
	yo : float := float(oyc+j) -0.5 - yycc;
	zo : float := float(ozc+k) -0.5 - zzcc;

----------- begin addendum -------------------------

	-- centroids before offset to puzzle center:
	xcc : float := float(ixc+i) -0.5;
	ycc : float := float(iyc+j) -0.5;
	zcc : float := float(izc+k) -0.5;

	fr : float := 0.06; -- move cubelet-center 3% toward PP-center
	dx : float := fr*(float(ixc+cx) - xcc);
	dy : float := fr*(float(iyc+cy) - ycc);
	dz : float := fr*(float(izc+cz) - zcc);

	-- this is to slightly separate the puzzle pieces
	-- but not their constituent cubelets.

----------- end addendum -------------------------


	sc: float := 0.47; --reduce radius from 0.5


	r,g,b, tt, xt,yt,zt : float;

begin

	if ii=1 then
		r:=0.2; g:=1.0; b:=0.2;
	elsif ii=2 then
		r:=1.0; g:=1.0; b:=0.2;
	elsif ii=3 then
		r:=0.2; g:=1.0; b:=1.0;
	elsif ii=4 then
		r:=1.0; g:=1.0; b:=1.0;
	elsif ii=5 then
		r:=0.2; g:=0.2; b:=0.2;
	elsif ii=6 then
		r:=1.0; g:=0.2; b:=0.3;
	elsif ii=7 then
		r:=0.2; g:=0.2; b:=1.0;
	elsif ii=8 then
		r:=1.0; g:=0.2; b:=1.0;
	elsif ii=9 then
		r:=0.6; g:=0.6; b:=0.6;
	end if;

	if grinding(ii) then
		tt := (nowTime-movebegin)/movedelay;
		if tt>1.0 then
			pictobj.setrect( po, xc+dx,yc+dy,zc+dz, sc,sc,sc, r,g,b);
			grinding(ii):=false;
		else
			xt := xo + tt*(xc-xo);
			yt := yo + tt*(yc-yo);
			zt := zo + tt*(zc-zo);
			pictobj.setrect( po, xt+dx,yt+dy,zt+dz, sc,sc,sc, r,g,b);
		end if;
	else
		pictobj.setrect( po, xc+dx,yc+dy,zc+dz, sc,sc,sc, r,g,b);
	end if;

	pictobj.draw( po, vertbuff, rgbbuff, uvbuff, elembuff );

end drawCube;





--------------------------------------------------------------------------

--type vtype is array(0..1,0..1,0..1) of boolean;
--wake, probe : vtype;






function tryup(ii: rngp) return boolean is -- +Y
	ix,iy,iz : integer;
	ox,oy,oz : integer;
begin
	ox:=centroid(ii)(1);
	oy:=centroid(ii)(2);
	oz:=centroid(ii)(3);

	-- next centroid:
	ix:=ox;
	iy:=oy+1;
	iz:=oz;


	if iy+1>my then
		return false;

	elsif ii=5 or ii=4 or ii=3 then

		if
			empty( ix+0,iy+1 ) and
			empty( ix+1,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=2 then

		if
			empty( ix+1,iy+1 ) and
			empty( ix+0,iy+0 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=1 then

		if
			empty( ix+0,iy+1 ) and
			empty( ix+1,iy+0 )
		then
			return true;
		else
			return false;
		end if;

	else
		return false;

	end if;

end tryup;












function trydown(ii: rngp) return boolean is -- -Y
	ix,iy,iz : integer;
	ox,oy,oz : integer;
begin
	ox:=centroid(ii)(1);
	oy:=centroid(ii)(2);
	oz:=centroid(ii)(3);

	-- next centroid:
	ix:=ox;
	iy:=oy-1;
	iz:=oz;


	if iy<1 then
		return false;

	elsif ii=5 or ii=2 or ii=1 then

		if
			empty( ix+0,iy+0 ) and
			empty( ix+1,iy+0 )
		then
			return true;
		else
			return false;
		end if;



	elsif ii=4 then

		if
			empty( ix+1,iy+0 ) and
			empty( ix+0,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=3 then

		if
			empty( ix+0,iy+0 ) and
			empty( ix+1,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	else
		return false;


	end if;

end trydown;

















function tryright(ii: rngp) return boolean is -- +X
	ix,iy,iz : integer;
	ox,oy,oz : integer;
begin
	ox:=centroid(ii)(1);
	oy:=centroid(ii)(2);
	oz:=centroid(ii)(3);

	-- next centroid:
	ix:=ox+1;
	iy:=oy;
	iz:=oz;


	if ix+1>mx then -- illegal pos
		return false;

	elsif ii=5 or ii=4 or ii=2 then -- skull or LR-L or UR-L

		if
			empty( ix+1,iy+0 ) and -- need 2 aligned blanks on right
			empty( ix+1,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=3 then -- LL-L

		if
			empty( ix+1,iy+1 ) and -- need 2 offset blanks on right
			empty( ix+0,iy+0 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=1 then -- UL-L

		if
			empty( ix+1,iy+0 ) and -- need 2 offset blanks on right
			empty( ix+0,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	else
		return false;

	end if;

end tryright;
















function tryleft(ii: rngp) return boolean is -- -X
	ix,iy,iz : integer;
	ox,oy,oz : integer;
begin
	ox:=centroid(ii)(1);
	oy:=centroid(ii)(2);
	oz:=centroid(ii)(3);

	-- next centroid:
	ix:=ox-1;
	iy:=oy;
	iz:=oz;

	if ix<1 then -- illegal pos
		return false;

	elsif ii=5 or ii=3 or ii=1 then -- skull or LL-L or UL-L

		if
			empty( ix+0,iy+0 ) and -- need 2 aligned blanks on left
			empty( ix+0,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=4 then -- LR-L

		if
			empty( ix+0,iy+1 ) and -- need 2 offset blanks on left
			empty( ix+1,iy+0 )
		then
			return true;
		else
			return false;
		end if;

	elsif ii=2 then -- UR-L

		if
			empty( ix+0,iy+0 ) and -- need 2 offset blanks on left
			empty( ix+1,iy+1 )
		then
			return true;
		else
			return false;
		end if;

	else
		return false;

	end if;

end tryleft;


---------------------------------------------------------------------------






procedure moveleft( kk: in out integer; nowTime: float ) is -- -X
	ix,iy,iz : integer;
begin


if not tryleft(kk) then
	for i in 1..5 loop
		if tryleft(i) then
			kk:=i;
			exit;
		end if;
	end loop;
end if;

if tryleft(kk) then

	oxc:=centroid(kk)(1);
	oyc:=centroid(kk)(2);
	ozc:=centroid(kk)(3);

	ix:=centroid(kk)(1);
	iy:=centroid(kk)(2);
	iz:=centroid(kk)(3);

	if kk=5 then -- (easy)

		-- trailing face aligned:
		empty( ix+1,iy+0 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move left:
		centroid(kk)(1):=centroid(kk)(1)-1;
		ix:=centroid(kk)(1);

		-- leading face aligned:
		empty( ix+0,iy+0 ):=false;
		empty( ix+0,iy+1 ):=false;


	elsif kk=4 then -- LR-L

		-- trailing face aligned:
		empty( ix+1,iy+0 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move left:
		centroid(kk)(1):=centroid(kk)(1)-1;
		ix:=centroid(kk)(1);

		-- leading face offset:
		empty( ix+0,iy+1 ):=false;
		empty( ix+1,iy+0 ):=false;

	elsif kk=3 then -- LL-L

		-- trailing face offset:
		empty( ix+1,iy+1 ):=true;
		empty( ix+0,iy+0 ):=true;

		-- make the move left:
		centroid(kk)(1):=centroid(kk)(1)-1;
		ix:=centroid(kk)(1);

		-- leading face aligned:
		empty( ix+0,iy+0 ):=false;
		empty( ix+0,iy+1 ):=false;

	elsif kk=2 then -- UR-L

		-- trailing face aligned:
		empty( ix+1,iy+0 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move left:
		centroid(kk)(1):=centroid(kk)(1)-1;
		ix:=centroid(kk)(1);

		-- leading face offset:
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=1 then -- UL-L

		-- trailing face offset:
		empty( ix+1,iy+0 ):=true;
		empty( ix+0,iy+1 ):=true;

		-- make the move left:
		centroid(kk)(1):=centroid(kk)(1)-1;
		ix:=centroid(kk)(1);

		-- leading face aligned:
		empty( ix+0,iy+0 ):=false;
		empty( ix+0,iy+1 ):=false;

	end if;


	test4winner;

	if not mute then 
	snd4ada.playSnd(brick); 
	end if;

	movebegin:=nowTime;
	grinding(kk):=true;

end if; --tryleft


end moveleft;




procedure moveright( kk: in out integer; nowTime: float ) is -- +X
	ix,iy,iz : integer;
begin

if not tryright(kk) then
	for i in 1..5 loop
		if tryright(i) then
			kk:=i;
			exit;
		end if;
	end loop;
end if;


if tryright(kk) then

	oxc:=centroid(kk)(1);
	oyc:=centroid(kk)(2);
	ozc:=centroid(kk)(3);

	ix:=centroid(kk)(1);
	iy:=centroid(kk)(2);
	iz:=centroid(kk)(3);

	if kk=5 then -- (easy)

		-- trailing face
		empty( ix+0,iy+0 ):=true;
		empty( ix+0,iy+1 ):=true;

		-- make the move:
		centroid(kk)(1):=centroid(kk)(1)+1;
		ix:=centroid(kk)(1);

		-- leading face
		empty( ix+1,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=4 then

		-- trailing face:
		empty( ix+1,iy+0 ):=true;
		empty( ix+0,iy+1 ):=true;

		-- make the move:
		centroid(kk)(1):=centroid(kk)(1)+1;
		ix:=centroid(kk)(1);

		-- leading face:
		empty( ix+1,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=3 then

		-- trailing face:
		empty( ix+0,iy+0 ):=true;
		empty( ix+0,iy+1 ):=true;

		-- make the move:
		centroid(kk)(1):=centroid(kk)(1)+1;
		ix:=centroid(kk)(1);

		-- leading face:
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=2 then

		-- trailing face:
		empty( ix+0,iy+0 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move:
		centroid(kk)(1):=centroid(kk)(1)+1;
		ix:=centroid(kk)(1);

		-- leading face:
		empty( ix+1,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=1 then

		-- trailing face:
		empty( ix+0,iy+0 ):=true;
		empty( ix+0,iy+1 ):=true;

		-- make the move:
		centroid(kk)(1):=centroid(kk)(1)+1;
		ix:=centroid(kk)(1);

		-- leading face:
		empty( ix+0,iy+1 ):=false;
		empty( ix+1,iy+0 ):=false;


	end if;



	test4winner;

	if not mute then 
	snd4ada.playSnd(brick); 
	end if;

	movebegin:=nowTime;
	grinding(kk):=true;

end if; --tryright


end moveright;






procedure movedown( kk: in out integer; nowTime: float ) is -- -Y
	ix,iy,iz : integer;
begin

if not trydown(kk) then
	for i in 1..5 loop
		if trydown(i) then
			kk:=i;
			exit;
		end if;
	end loop;
end if;


if trydown(kk) then

	oxc:=centroid(kk)(1);
	oyc:=centroid(kk)(2);
	ozc:=centroid(kk)(3);

	ix:=centroid(kk)(1);
	iy:=centroid(kk)(2);
	iz:=centroid(kk)(3);

	if kk=5 then -- (easy)

		--trailing
		empty( ix+0,iy+1 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)-1;
		iy:=centroid(kk)(2);

		--leading
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+0 ):=false;


	elsif kk=4 then

		--trail
		empty( ix+0,iy+1 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)-1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+1,iy+0 ):=false;
		empty( ix+0,iy+1 ):=false;

	elsif kk=3 then

		--trail
		empty( ix+0,iy+1 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)-1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=2 then

		--trail
		empty( ix+1,iy+1 ):=true;
		empty( ix+0,iy+0 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)-1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+0 ):=false;

	elsif kk=1 then

		--trail
		empty( ix+0,iy+1 ):=true;
		empty( ix+1,iy+0 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)-1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+0 ):=false;

	end if;


	test4winner;

	if not mute then 
	snd4ada.playSnd(brick); 
	end if;

	movebegin:=nowTime;
	grinding(kk):=true;

end if; --trydown


end movedown;



procedure moveup( kk: in out integer; nowTime: float ) is -- +Y
	ix,iy,iz : integer;
begin

if not tryup(kk) then
	for i in 1..5 loop
		if tryup(i) then
			kk:=i;
			exit;
		end if;
	end loop;
end if;



if tryup(kk) then

	oxc:=centroid(kk)(1);
	oyc:=centroid(kk)(2);
	ozc:=centroid(kk)(3);

	ix:=centroid(kk)(1);
	iy:=centroid(kk)(2);
	iz:=centroid(kk)(3);

	if kk=5 then -- (easy)

		-- trailing face:
		empty( ix+0,iy+0 ):=true;
		empty( ix+1,iy+0 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)+1;
		iy:=centroid(kk)(2);

		-- leading face:
		empty( ix+0,iy+1 ):=false;
		empty( ix+1,iy+1 ):=false;


	elsif kk=4 then

		--trail
		empty( ix+0,iy+1 ):=true;
		empty( ix+1,iy+0 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)+1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+1 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=3 then

		--trail
		empty( ix+0,iy+0 ):=true;
		empty( ix+1,iy+1 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)+1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+1 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=2 then

		--trail
		empty( ix+0,iy+0 ):=true;
		empty( ix+1,iy+0 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)+1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+0 ):=false;
		empty( ix+1,iy+1 ):=false;

	elsif kk=1 then

		--trail
		empty( ix+0,iy+0 ):=true;
		empty( ix+1,iy+0 ):=true;

		-- make the move:
		centroid(kk)(2):=centroid(kk)(2)+1;
		iy:=centroid(kk)(2);

		--lead
		empty( ix+0,iy+1 ):=false;
		empty( ix+1,iy+0 ):=false;

	end if;




	test4winner;

	if not mute then 
	snd4ada.playSnd(brick); 
	end if;

	movebegin:=nowTime;
	grinding(kk):=true;

end if; --tryup


end moveup;









	wintime, currentTime : float;

	v4, vcc : vec4;


	xize: float := 0.25;

	ix,iy,iz : integer := 1;


	-- I shall first attempt to treat OS-X the same as GNU-Linux
	-- given that like-binaries will be together in same directory:
	--dirname: constant string := ada.directories.current_directory;
	--nexname: constant string := dirname & "/reliquarium";
	--Ok: boolean;


	osel, ixsel: integer:=1;
	pik: boolean := false;


	hc: boolean := true; --HighContrast

----------------- main program begin ==========================
begin --tomb

--put_line("Entering tomb2");

	first_prep;  -- main program setup

	show_axes:=true;



	degRotate( mm, 20.0, 1.0, 0.0, 0.0 );




	-- note:  only mm changes:
	updateMVP( float(wwid), float(whit) );




	-- main event loop begin: ------------------------------------------
   while not userexit loop

------- begin response to keys ------------------------------------------

		currentTime := float(glfwGetTime);
		--deltaT := currentTime - oldTimeKb;

		GlfwPollEvents;



		--check for <esc>,<q>:
		if glfwgetkey( mainWin, glfw_key_escape ) = Glfw_Press then
				userexit:=true;

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

		end if;

		exit when glfwWindowShouldClose(mainWin) /= 0; --19oct21





		anyGrinding:=false;
		for i in rngp loop
			anyGrinding := anyGrinding or grinding(i);
		end loop;

		if not anyGrinding then

osel:=ixsel;

			gameutils.getKeyInputs(
				mainWin,
				rngp'first, rngp'last,
				ixsel,pik);

if ixsel/=osel then
	--put_line("KEY chose isel="&integer'image(ixsel));
	null;
end if;


			if goUp then
				goUp:=false;
				moveUp(ixsel,currentTime);
			elsif goDown then
				goDown:=false;
				moveDown(ixsel,currentTime);
			elsif goLeft then
				goLeft:=false;
				moveLeft(ixsel,currentTime);
			elsif goRight then
				goRight:=false;
				moveRight(ixsel,currentTime);
			--elsif goAway then
			--	goAway:=false;
			--	moveAway(ixsel,currentTime);
			--elsif goNear then
			--	goNear:=false;
			--	moveNear(ixsel,currentTime);
			elsif doRestart then
				doRestart:=false;
				centroid:=centroidHi;
				initEmptyHi;
			elsif tryNextSkin then
				tryNextSkin:=false;
				setNextSkin;
			end if;

osel:=ixsel;
			gameutils.handle_mouse(
				pik, 
				rngp'first, rngp'last, 
				ixsel,
				centroid,
				xxcc,yycc,zzcc
				);

if ixsel/=osel then
	--put_line("mouse chose isel="&integer'image(ixsel));
	null;
end if;

		end if;

----///////////////////// end response to key/mouse


		updateMVP( float(wwid), float(whit) );


-------- here we should handle resized window ----------------------


		glfwGetWindowSize( mainWin, Nwid'access, Nhit'access );
		if( (Nwid /= wwid) or (Nhit /= whit) ) then
			wwid:=Nwid;  whit:=Nhit;

			glfwGetFramebufferSize(mainwin, fwid'access, fhit'access);
			glViewport(0,0,Fwid,Fhit);

		end if;



--------- begin drawing ===============================================

		glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);



------ draw here ----------------------
		-- all the 4 puzzle pieces:
		glUseProgram( pgmTexShadID );
		gluniformmatrix4fv( matrixid, 1, gl_false, mvp(1,1)'address );
		gluniform1i(uniftex,0);

		for ii in rngp loop -- 4 corners + 1 egg

			if numerals then
				glBindTexture(gl_texture_2d, num_texid(ii) );
			else
				glBindTexture(gl_texture_2d, num_texid(0) );
			end if;

			ix:=centroid(ii)(1);
			iy:=centroid(ii)(2);
			iz:=centroid(ii)(3);


			if ii = rngp'last then --egg

				--draw5(ppo, currentTime);
				null;

			elsif ii=1 then --UL
				drawCube(ppo,ii, 0,0,0, 0,0,0, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 0,0,0, 1,0,0, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 0,0,0, 0,1,0, ix,iy,iz, currentTime);

				drawCube(ppo,ii, 0,0,1, 0,0,1, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 0,0,1, 1,0,1, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 0,0,1, 0,1,1, ix,iy,iz, currentTime);

			elsif ii=2 then --UR
				drawCube(ppo,ii, 1,0,0, 0,0,0, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 1,0,0, 1,0,0, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 1,0,0, 1,1,0, ix,iy,iz, currentTime);

				drawCube(ppo,ii, 1,0,1, 0,0,1, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 1,0,1, 1,0,1, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 1,0,1, 1,1,1, ix,iy,iz, currentTime);

			elsif ii=3 then --LL
				drawCube(ppo,ii, 0,1,0, 0,0,0, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 0,1,0, 0,1,0, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 0,1,0, 1,1,0, ix,iy,iz, currentTime);

				drawCube(ppo,ii, 0,1,1, 0,0,1, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 0,1,1, 0,1,1, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 0,1,1, 1,1,1, ix,iy,iz, currentTime);

			elsif ii=4 then -- LR
				drawCube(ppo,ii, 1,1,0, 1,0,0, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 1,1,0, 1,1,0, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 1,1,0, 0,1,0, ix,iy,iz, currentTime);

				drawCube(ppo,ii, 1,1,1, 1,0,1, ix,iy,iz, currentTime);
				drawCube(ppo,ii, 1,1,1, 1,1,1, ix,iy,iz, currentTime);--corner
				drawCube(ppo,ii, 1,1,1, 0,1,1, ix,iy,iz, currentTime);

			end if;


		end loop; --ii


		glUseProgram( pgmTexID );
		gluniformmatrix4fv( matid, 1, gl_false, mvp(1,1)'address );
		gluniform1i(unitex,0);

		if not winner then
			glBindTexture(gl_texture_2d, box_texid ); 
			drawBigCube(ppo2); --bounding chainlink box
		end if;

		glBindTexture(gl_texture_2d, num_texid(5) ); --skull
		draw5(ppo, currentTime);






		if show_axes then

			v4 := (+2.0, -3.0, -1.0, 1.0);
			matXvec(mvp, v4, vcc);
			stex.print3d("+X", vcc(1), vcc(2), vcc(3), vcc(4), 1.0, 1.0);

			v4 := (-2.0, +3.0, -1.0, 1.0);
			matXvec(mvp, v4, vcc);
			stex.print3d("+Y", vcc(1), vcc(2), vcc(3), vcc(4), 1.0, 1.0);

			v4 := (-2.0, -3.0, +1.0, 1.0);
			matXvec(mvp, v4, vcc);
			stex.print3d("+Z", vcc(1), vcc(2), vcc(3), vcc(4), 1.0, 1.0);

			v4 := (-2.0, -3.0, -1.0, 1.0);
			matXvec(mvp, v4, vcc);
			stex.print3d("O", vcc(1), vcc(2), vcc(3), vcc(4), 1.0, 1.0);

		end if;



		xize:=0.25;


		-- fixed 2d location text:
		stex.print2d("(spc)=>Restart", 0.02, 0.05, xize );
		stex.print2d("(i,o,mousewheel)=>Zoom", 0.36, 0.05, xize);
		stex.print2d("(esc)=>exit",    0.78, 0.05, xize );
		stex.print2d("1-4=>pick",      0.02, 0.02, xize );
		stex.print2d("s=>skull",        0.20, 0.02, xize );
		stex.print2d("n=>nums",        0.40, 0.02, xize ); --8oct21
		stex.print2d("c=>nextColor",        0.58, 0.02, xize );
		stex.print2d("h=>help",        0.85, 0.02, xize );




		if help then

			xize:=0.4; hc:=false;
			stex.print2d("Reckless raiders have plundered a reliquary",0.02,0.90,xize,hc);
			stex.print2d("...and displaced an ancient Crystal Skull !",0.02,0.85,xize,hc);

			stex.print2d("Return the Skull to its proper position", 0.02, 0.80, xize,hc);
			stex.print2d("Centered within its square Framework.", 0.02, 0.75, xize,hc);

			stex.print2d("With X rightward, Y upward, and Z outward,", 0.02, 0.70, xize,hc);
			stex.print2d("use the keys: u/d  l/r ", 0.02, 0.65, xize,hc);
			stex.print2d("(Up/Down Left/Right or arrow keys)", 0.02, 0.60, xize,hc);

			stex.print2d("Select a block by number or mouse-click...", 0.02, 0.50, xize,hc);
			stex.print2d("Laptop users can place cursor on block", 0.02, 0.45, xize,hc);
			stex.print2d("then hit <enter>-key to select", 0.02, 0.40, xize,hc);

			stex.print2d("Zoom keys:   i/o = In/Out", 0.02, 0.35, xize,hc);

			stex.print2d("V-key to mute moves", 0.02, 0.30, xize,hc);

			xize:=0.25;

		else
			stex.print2d("Moderate:   Move the skull to the center of its square frame.",0.10,0.95,xize);
		end if;

		if winner and (currentTime-testime>windelay) then
			stex.print2d("Correct!", 0.19, 0.83, 1.0 );

			if not playedonce then
		 		snd4ada.playSnd(fanfare); --fanfare
				playedonce:=true;
				wintime:=currentTime;
			end if;

			if( currentTime-wintime > 5.0 ) then 
				winner:=false;
			end if;

		else
			playedonce:=false;
		end if;


--------- end drawing =================================================



		glflush;
		glfwSwapBuffers( mainWin );


   end loop; -------------------- main event loop end -------------------



	glext.binding.glDeleteProgram(pgmtexshadid);
	glext.binding.glDeleteProgram(pgmtexid);

	glext.binding.glDeleteBuffers(1, vertbuff'address);
	glext.binding.glDeleteBuffers(1, rgbbuff'address);
	glext.binding.glDeleteBuffers(1, elembuff'address);
	glext.binding.glDeleteBuffers(1, uvbuff'address);

	glext.binding.glDeleteVertexArrays(1, vertexarrayid'address);

	stex.CloseFont;

	snd4ada.termSnds;

	glfwdestroywindow(mainWin);
	glfwTerminate;

--put_line("Exitting tomb2");


end tomb2;


