

--
-- Copyright (C) 2020  <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/>.
--




with bfs7;
with ada.strings.unbounded;

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;
with zoomwheel;

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

with matutils;
with stex;

with ada.numerics.generic_elementary_functions;

with unchecked_deallocation;

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

with shader;  use shader;
with dumpgl;

with Ada.Numerics.Float_Random;
with Ada.Directories;
with ada.strings.fixed;
with Ada.Command_Line;
----------------------------------------------------------------



procedure seven is

	G : Ada.Numerics.Float_Random.Generator;


	prep_error : exception;
	cube_error : exception;


	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;

	use ada.strings.unbounded;

	use glfw3;
	use dumpgl;

	-- mm = modelmatrix,
	-- vm = viewmatrix,
	-- pm = perspectivematrix,
	--
	mv, vm, mvp, pm, mm : mat44 := identity;
	origskin : boolean := true; -- false => Vadasz colors
	mute,dump: boolean := false; -- print layout

	mainWin : access GLFWwindow;


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

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

		--            eye             cen           up
		lookat(vm, 0.0,0.0,eye,  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; code:integer:=0 ) is
begin
  if condition=false then
  		text_io.put_line("Assertion Failed #"&integer'image(code));
  		raise program_error;
  end if;
end myassert;






procedure InitGLFW( 
	wid, hit : in 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 GLFW3...");

	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 );



	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);

	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;





rundirstr : string := ada.directories.current_directory;


procedure first_prep is -- main program setup
	--fwid,fhit: aliased glint;
begin

	snd4ada.initSnds;

	fanfare := snd4ada.initSnd(
		--Interfaces.C.Strings.New_String("data/fanfare.wav"),90);
		Interfaces.C.Strings.New_String(rundirstr&"/"&"data/fanfare.wav"));

	whoosh := snd4ada.initSnd(
		--Interfaces.C.Strings.New_String("data/whoosh_4th.wav"),90);
		Interfaces.C.Strings.New_String(rundirstr&"/"&"data/whoosh_4th.wav"));


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






	winwidth  := 600;
	winheight := 600;


	if origskin then
		InitGLFW(winwidth,winheight,Fwid,Fhit,"Ada7");
	else
		InitGLFW(winwidth,winheight,Fwid,Fhit,"Vadasz");
	end if;

	zoomwheel.enable(mainWin);

	glViewport(0,0,fwid,fhit);


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

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

	glgenbuffers(1, vertexbuff'address);
	glgenbuffers(1, colorbuff'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);

end first_prep;















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



dimen : constant integer := 2;
ncubes  : constant integer := dimen*dimen*dimen;

subtype rngs is integer range 1..dimen;
subtype rngm is integer range 1..ncubes;
type permtype is array(rngs,rngs,rngs) of rngm;

perm : permtype;
brow, bcol, blay : rngs := 2;
winner, hint, help, show_axes : boolean := false;

xxx,yyy,zzz : array(rngm) of float;

red,grn,blu : array(1..ncubes*6) of float; -- for Rufas cube colors
red8,grn8,blu8 : array(1..ncubes) of float; -- for RGB cube colors
--rgb : boolean := false;

subtype str1 is string(1..1);
symbol : constant array(1..ncubes) of str1
     := ( "1", "2", "3",   "4", "5", "6",   "7", " " );




ntri  : constant integer := 12*(ncubes);
ncorners : constant integer := 3*ntri;
nvert: constant integer := 3*ncorners; -- 3*3*12*8
mvert: constant integer := nvert-108;   -- 3*3*12*7

type varray is array(1..nvert) of aliased float;
type vap is access varray;

vertices, colors : vap;
-- these are initialized/reset in "cubic"



procedure vfree is new unchecked_deallocation(varray,vap);





function indx(row,col,lay:rngs) return rngm is
begin
	return (row-1)*dimen*dimen + (col-1)*dimen + lay;
end indx;












firstcallcubic : boolean := true;

	-- this is where the vertex,color data is loaded:
procedure cubic( 
	rr : float;  
	perm : permtype
	) is separate;





function xtest4winner return boolean is
	localwinner: boolean := (brow=2) and (bcol=2) and (blay=2);
begin
	for row in rngS loop
	for col in rngS loop
	for lay in rngS loop
		localwinner := localwinner and 
			(perm(row,col,lay) = indx(row,col,lay));
	end loop;
	end loop;
	end loop;
	return localwinner;
end xtest4winner;


function manhattan( row1,col1,lay1, row2,col2,lay2 : integer ) return integer is
begin
	return abs(row1-row2)+abs(col1-col2)+abs(lay1-lay2);
end manhattan;


procedure test4winner is
	r,c,l : array(1..8) of integer; -- ordered coords of blocks
	oldwin : boolean := xtest4winner;
	n : integer;
	a1,a2,a3, b1,b2,b3,
	c1,c2,c3, d1,d2,d3,
	e1,e2,e3, f1,f2,f3,
	g1,g2,g3, h1,h2,h3,
	i1,i2,i3, j1,j2,j3,
	k1,k2,k3, l1,l2,l3 : boolean;
begin

if not origskin then
-- this option has only 1 winning config because each
-- cubelet has uniquely colored faces

	winner := xtest4winner;


else
-- the original rgbskin option has multiple winning configs because each
-- cubelet has a uniform color on all faces



	for row in rngS loop
	for col in rngS loop
	for lay in rngS loop
		n:=perm(row,col,lay);
		r(n):=row;
		c(n):=col;
		l(n):=lay;
	end loop;
	end loop;
	end loop;


	--normal winner:
	a1 := (r(2)=1) and (c(2)=1) and (l(2)=2);
	a2 := (r(3)=1) and (c(3)=2) and (l(3)=1);
	a3 := (r(5)=2) and (c(5)=1) and (l(5)=1);

	--1st alternate winner:
	b1 := (r(2)=2) and (c(2)=1) and (l(2)=1);
	b2 := (r(3)=2) and (c(3)=2) and (l(3)=2);
	b3 := (r(5)=1) and (c(5)=1) and (l(5)=2);

	--2nd alternate winner:
	c1 := (r(2)=2) and (c(2)=1) and (l(2)=1);
	c2 := (r(3)=1) and (c(3)=2) and (l(3)=1);
	c3 := (r(5)=2) and (c(5)=2) and (l(5)=2);

	--3rd alternate winner:
	d1 := (r(2)=1) and (c(2)=2) and (l(2)=1);
	d2 := (r(3)=2) and (c(3)=1) and (l(3)=1);
	d3 := (r(5)=1) and (c(5)=1) and (l(5)=2);

	--4th alternate winner:
	e1 := (r(2)=2) and (c(2)=1) and (l(2)=1);
	e2 := (r(3)=1) and (c(3)=1) and (l(3)=2);
	e3 := (r(5)=1) and (c(5)=2) and (l(5)=1);

	--5th alternate winner:
	f1 := (r(2)=2) and (c(2)=2) and (l(2)=2);
	f2 := (r(3)=1) and (c(3)=1) and (l(3)=2);
	f3 := (r(5)=2) and (c(5)=1) and (l(5)=1);

	--6th alternate winner:
	g1 := (r(2)=1) and (c(2)=1) and (l(2)=2);
	g2 := (r(3)=2) and (c(3)=1) and (l(3)=1);
	g3 := (r(5)=2) and (c(5)=2) and (l(5)=2);

	--7th alternate winner:
	h1 := (r(2)=1) and (c(2)=2) and (l(2)=1);
	h2 := (r(3)=2) and (c(3)=2) and (l(3)=2);
	h3 := (r(5)=2) and (c(5)=1) and (l(5)=1);

	--8th alternate winner:
	i1 := (r(2)=2) and (c(2)=2) and (l(2)=2);
	i2 := (r(3)=2) and (c(3)=1) and (l(3)=1);
	i3 := (r(5)=1) and (c(5)=2) and (l(5)=1);

	--9th alternate winner:
	j1 := (r(2)=1) and (c(2)=2) and (l(2)=1);
	j2 := (r(3)=1) and (c(3)=1) and (l(3)=2);
	j3 := (r(5)=2) and (c(5)=2) and (l(5)=2);

	--10th alternate winner:
	k1 := (r(2)=1) and (c(2)=1) and (l(2)=2);
	k2 := (r(3)=2) and (c(3)=2) and (l(3)=2);
	k3 := (r(5)=1) and (c(5)=2) and (l(5)=1);

	--11th alternate winner:
	l1 := (r(2)=2) and (c(2)=2) and (l(2)=2);
	l2 := (r(3)=1) and (c(3)=2) and (l(3)=1);
	l3 := (r(5)=1) and (c(5)=1) and (l(5)=2);


-- note:  I think 12 winner configurations is all of them...
-- there seems to be 4 out of 8 possible corners locations for the
-- black cubelet, and for each of those there are 3 axis rotations 
-- for RGB that maintain a right handed orientation.



	winner := 
	
		( 
			(a1 and a2 and a3) or 
			(b1 and b2 and b3) or
			(c1 and c2 and c3) or
			(d1 and d2 and d3) or
			(e1 and e2 and e3) or
			(f1 and f2 and f3) or
			(g1 and g2 and g3) or
			(h1 and h2 and h3) or
			(i1 and i2 and i3) or
			(j1 and j2 and j3) or
			(k1 and k2 and k3) or
			(l1 and l2 and l3) 
		) and

		-- test lower layer completely
		( 1 = manhattan(r(1),c(1),l(1), r(2),c(2),l(2)) ) and
		( 1 = manhattan(r(1),c(1),l(1), r(3),c(3),l(3)) ) and
		( 2 = manhattan(r(1),c(1),l(1), r(4),c(4),l(4)) ) and
		( 2 = manhattan(r(2),c(2),l(2), r(3),c(3),l(3)) ) and

		-- test nearest neighbors in different layers
		( 1 = manhattan(r(1),c(1),l(1), r(5),c(5),l(5)) ) and
		( 1 = manhattan(r(2),c(2),l(2), r(6),c(6),l(6)) ) and
		( 1 = manhattan(r(3),c(3),l(3), r(7),c(7),l(7)) ) and
		( 1 = manhattan(r(4),c(4),l(4), r(8),c(8),l(8)) ) and

		-- test opposite corners
		( 3 = manhattan(r(1),c(1),l(1), r(8),c(8),l(8)) ) and
		( 3 = manhattan(r(2),c(2),l(2), r(7),c(7),l(7)) ) and
		( 3 = manhattan(r(3),c(3),l(3), r(6),c(6),l(6)) ) and
		( 3 = manhattan(r(4),c(4),l(4), r(5),c(5),l(5)) );

	if not winner and oldwin then
		put_line("winner test alert:  new fails, old passes");
	--elsif winner and not oldwin then
	--	put_line("winner test alert:  old fails, new passes");
	end if;

	-- In case there are other winning configs that are not yet recognized...
	-- the (x)-key will toggle this dump:
	if dump then -- if this config should be a winner, add it!
		put("  red:"&integer'image(r(2))&","&integer'image(c(2))&","&integer'image(l(2)));
		put("  grn:"&integer'image(r(3))&","&integer'image(c(3))&","&integer'image(l(3)));
		put("  blu:"&integer'image(r(5))&","&integer'image(c(5))&","&integer'image(l(5)));
		new_line;
	end if;

end if; -- origskin

end test4winner;






   procedure moveZm is
   -- move space away (to a bigger layer#)
   begin
     if blay<dimen then
       perm(brow,bcol,blay):=perm(brow,bcol,blay+1);
       blay:=blay+1;
       perm(brow,bcol,blay):=8;
		 test4winner;

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

     end if;
   end moveZm;

   procedure moveZp is
   -- move space closer (to a smaller layer#)
   begin
     if blay>1 then
       perm(brow,bcol,blay):=perm(brow,bcol,blay-1);
       blay:=blay-1;
       perm(brow,bcol,blay):=8;
		 test4winner;

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

     end if;
   end moveZp;

   procedure moveXm is
   --move space to a bigger col#
   begin
     if bcol<dimen then
       perm(brow,bcol,blay):=perm(brow,bcol+1,blay);
       bcol:=bcol+1;
       perm(brow,bcol,blay):=8;
		 test4winner;

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

     end if;
   end moveXm;

   procedure moveXp is
   --move space to a smaller col#
   begin
     if bcol>1 then
       perm(brow,bcol,blay):=perm(brow,bcol-1,blay);
       bcol:=bcol-1;
       perm(brow,bcol,blay):=8;
		 test4winner;

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

     end if;
   end moveXp;

   procedure moveYp is
   --move space to a smaller row#
   begin
     if brow>1 then
       perm(brow,bcol,blay):=perm(brow-1,bcol,blay);
       brow:=brow-1;
       perm(brow,bcol,blay):=8;
		 test4winner;

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

     end if;
   end moveYp;

   procedure moveYm is
   --move space to a bigger row#
   begin
     if brow<dimen then
       perm(brow,bcol,blay):=perm(brow+1,bcol,blay);
       brow:=brow+1;
       perm(brow,bcol,blay):=8;
		 test4winner;

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

     end if;
   end moveYm;

	--NOTE:  X=invCol, Y=invRow, Z=invLay !



	procedure restart is
	begin

     for row in rngS loop
       for col in rngS loop
         for lay in rngS loop
           perm(row,col,lay) := indx(row,col,lay);
	 		end loop;
       end loop;
     end loop;
     brow:=2;
     bcol:=2;
     blay:=2;

	end restart;






up : constant str1 := "u";
dn : constant str1 := "d";
lf : constant str1 := "l";
rt : constant str1 := "r";
nr : constant str1 := "n";
aw : constant str1 := "a";
sol : array(1..100_000) of str1;
nsol : integer := 0;
s: str1;

	haveSolution: integer := 0;
	solutionPath: unbounded_string;


   procedure shuffle( level: in integer ) is

	  r : Ada.Numerics.Float_Random.Uniformly_Distributed;

     n : integer := 0;
	  br1,br2,bc1,bc2,bl1,bl2: integer;
	  prev : str1 := "z";
	  msave: boolean := mute;
   begin

		haveSolution:=0;
		mute:=true;

		winner:=false;
     for row in rngS loop
       for col in rngS loop
         for lay in rngS loop
           perm(row,col,lay) := indx(row,col,lay);
	 		end loop;
       end loop;
     end loop;
     brow:=2;
     bcol:=2;
     blay:=2;

     case level is
     when 0 => n:=0;
     when 1 => n:=10;
     when 2 => n:=100;
     when 3 => n:=1000;
     when 4 => n:=10_000;
     when 5 => n:=100_000;
     when others => n:=0;
     end case;


-- here we use a "stack" of letters {l,r,u,d,n,a} to 
-- store solution for possible playback
-- WARNING:  for large n, stored solution is 
--           likely very far from optimal!

		nsol:=0;
     for i in 1..n loop

		 r := Ada.Numerics.Float_Random.Random(G);

       if r<0.16 and prev /= rt then
		 	br1:=brow; bc1:=bcol; bl1:=blay;
         moveXm;                                --attempted move
		 	br2:=brow; bc2:=bcol; bl2:=blay;
			if br2/=br1 or bc2/=bc1 or bl2/=bl1 then --attempt succeeded
				prev:=lf;
				nsol:=nsol+1;
				sol(nsol):=rt;
			end if;
       elsif r<0.33 and prev /= lf then
		 	br1:=brow; bc1:=bcol; bl1:=blay;
         moveXp;											--attempt
		 	br2:=brow; bc2:=bcol; bl2:=blay;
			if br2/=br1 or bc2/=bc1 or bl2/=bl1 then  --success
				prev:=rt;
				nsol:=nsol+1;
				sol(nsol):=lf;
			end if;
       elsif r<0.49 and prev /= dn then
		 	br1:=brow; bc1:=bcol; bl1:=blay;
         moveYm;
		 	br2:=brow; bc2:=bcol; bl2:=blay;
			if br2/=br1 or bc2/=bc1 or bl2/=bl1 then
				prev:=up;
				nsol:=nsol+1;
				sol(nsol):=dn;
			end if;
       elsif r<0.66 and prev /= up then
		 	br1:=brow; bc1:=bcol; bl1:=blay;
         moveYp;
		 	br2:=brow; bc2:=bcol; bl2:=blay;
			if br2/=br1 or bc2/=bc1 or bl2/=bl1 then
				prev:=dn;
				nsol:=nsol+1;
				sol(nsol):=up;
			end if;
       elsif r<0.82 and prev /= aw then
		 	br1:=brow; bc1:=bcol; bl1:=blay;
         moveZm;
		 	br2:=brow; bc2:=bcol; bl2:=blay;
			if br2/=br1 or bc2/=bc1 or bl2/=bl1 then
				prev:=nr;
				nsol:=nsol+1;
				sol(nsol):=aw;
			end if;
       elsif prev /= nr then
		 	br1:=brow; bc1:=bcol; bl1:=blay;
         moveZp;
		 	br2:=brow; bc2:=bcol; bl2:=blay;
			if br2/=br1 or bc2/=bc1 or bl2/=bl1 then
				prev:=aw;
				nsol:=nsol+1;
				sol(nsol):=nr;
			end if;
       end if;

     end loop;
	  mute:=msave;

put("#moves in shuffle:");
put(integer'image(nsol));
new_line;

   end shuffle;




procedure initperm is
begin

   for row in rngS loop
     for col in rngS loop
       for lay in rngS loop
           perm(row,col,lay) := indx(row,col,lay);
       end loop;
     end loop;
   end loop;
	brow:=2;
	bcol:=2;
	blay:=2;
	if perm(brow,bcol,blay) /= 8 then
		raise program_error;
	end if;
end initperm;




















function odd( i: integer ) return boolean is
begin
	return ( i mod 2 = 1 );
end odd;



function bitmatch( x, y : integer ) return boolean is
	result : boolean := false;
	a : integer := x;
	b : integer := y;
begin
	for i in 1..32 loop
		if ( odd(a) and odd(b) ) then result:=true; end if;
		a:=a/2;
		b:=b/2;
	end loop;
	return result;
end;





procedure draw( pid: gluint; mid, uid: glint; vertexbuff, colorbuff : gluint; nv: integer ) is
begin

	glUseProgram( pid );
	gluniformmatrix4fv( mid, 1, gl_false, mvp(1,1)'address );
	gluniform1i(uid,0);

	-- 0th attribute:  vertices
	glBindBuffer(gl_array_buffer, vertexbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*nv), vertices(1)'address, gl_static_draw);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0,3,gl_float,gl_false,0, system.null_address);

	-- 1st attribute:  color
	glBindBuffer(gl_array_buffer, colorbuff);
	glBufferData(gl_array_buffer, glsizeiptr(4*nv), colors(1)'address, gl_static_draw);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1,3,gl_float,gl_true,0, system.null_address);

	glDrawArrays( gl_triangles, 0, glint(nv) );

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);

end draw;








procedure permdump(fname: string) is -- 27feb21 experiment
	i,r,c,l: integer;
	row,col,lev: array(1..8) of integer;
	fout: text_io.file_type;
begin

	for r in 1..2 loop
	for c in 1..2 loop
	for l in 1..2 loop
		i:=perm(r,c,l);
		row(i):=r;
		col(i):=c;
		lev(i):=l;
	end loop;
	end loop;
	end loop;

	text_io.create(fout, out_file, fname);

	for i in 1..7 loop
		r:=row(i); c:=col(i); l:=lev(i);
		put(fout,integer'image(r));
		put(fout,integer'image(c));
		put(fout,integer'image(l));
		new_line(fout);
	end loop;

	r:=row(8); c:=col(8); l:=lev(8);
	put(fout,integer'image(r));
	put(fout,integer'image(c));
	put(fout,integer'image(l));
	new_line(fout);

	text_io.close(fout);

end permdump;










	playedonce : boolean := false;
	wintime, currentTime : gldouble;

	v4, vcc : vec4;

	ii,pp : integer;



	otitle : constant chars_ptr 
		:= ( new_string("Ada7") );
	vtitle : constant chars_ptr 
		:= ( new_string("Vadasz") );




	--fPM_id,
	--ftex_id,
	--fcol_id: glint;
	--fprog_id: gluint;
	fontcol: constant vec4 := (0.0,0.0,0.0,1.0); --black



	exeName: constant string := ada.command_line.command_name;
	onMac : boolean := ( ada.strings.fixed.index(exename,"_osx",1)>1 );







	xold,yold : aliased gldouble;

	btndlay: constant gldouble := 0.25; --mouseclick
	keydlay: constant gldouble := 0.25; --keybd
	oldTimeMs, --mousebtn
	oldTimeKb  --keybd
		: gldouble := 0.0;
	--prevTime : gldouble := 0.0; --mouseclick
	failureNotice, dragging, userexit: boolean := false;



procedure clickRight(msx,msy, Wwid, Whit : gldouble ) is separate;

procedure getMouseInputs( mainWin: access GLFWwindow; Wwid,Whit: gldouble ) is separate;
procedure getKeyInputs( mainWin : access GLFWwindow ) is separate;











	major,minor,rev : aliased int;


	sz: float := 0.4;
	hc: boolean := true;

----------------- main program begin ==========================
begin --seven

	Ada.Numerics.Float_Random.Reset(G); --randomize (time-dependent)

	origskin:=true;

	-- here we should process cmdline arg
   if Ada.Command_Line.Argument_Count > 0 then

     declare
       pstr : string := Ada.Command_Line.Argument(1);--v
     begin

		if pstr(1)='v' then
			origskin:=false;
		else
			origskin:=true;
		end if;

     end; --declare
   
   end if;



	new_line;

	vertices := new varray;
	colors := new varray;

	first_prep;  -- main program setup

-- works perfectly:
--put("MAIN:  calling playSnd @ 962");
--	snd4ada.playSnd(fanfare);
--put("...returned from playSnd @ 964");
--new_line;




	glfwGetVersion(major'access, minor'access, rev'access);
	put_line("GLFW version: "&int'image(major)&"."&int'image(minor));

	glgetintegerv(gl_major_version, major'address);
	glgetintegerv(gl_minor_version, minor'address);
	put_line("openGL version: "&int'image(major)&"."&int'image(minor));


	glGetIntegerv(GL_CONTEXT_PROFILE_MASK, profile'address);
	if( profile = GL_CONTEXT_CORE_PROFILE_BIT ) then
		put_line("ogl-query:  Core Profile");
	end if;

	-- OSX currently requires the forward_compatible flag!
	glGetIntegerv(GL_CONTEXT_FLAGS, flags'address);
	if( flags = GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT ) then
		put_line("ogl-query:  Forward-Compatible bit is set");
	end if;






	programid := 
		loadshaders(
			"data/vertshader330.glsl", 
			"data/fragshader330.glsl");
	matrixid  := glgetuniformlocation(programid, pmvp);
	uniftex   := glgetuniformlocation(programid, pmyts);


	initperm;

	cubic(1.0,perm);

	-- rotate into preferred initial orientation:
	degRotate( mm, 150.0, 1.0, 0.0, 0.0 ); -- about Xaxis



	-- note:  only mm changes:
	updateMVP( float(winwidth), float(winheight) );



	-- prepare font -------------
	stex.InitFont ( "data/NotoSans-Regular.ttf" );
	stex.setColor( fontcol );
	stex.reSize(winwidth,winheight);







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

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


		currentTime := glfwGetTime;

		GlfwPollEvents;

		getKeyInputs(mainWin);
		exit when userexit;
		getMouseInputs(mainWin,gldouble(winwidth),gldouble(winheight));
		exit when glfwWindowShouldClose(mainWin) /= 0; --14may21 addendum


		cubic( 1.0, perm );


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


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

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

		end if;

		updateMVP( float(winwidth), float(winheight) );


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

		glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);



------ draw here ----------------------

		-------------------------------------
		-- main display function
		-------------------------------------
		if winner and origskin then
			draw( programid, matrixid, uniftex, vertexbuff, colorbuff, nvert);
		else
			draw( programid, matrixid, uniftex, vertexbuff, colorbuff, mvert);
		end if;
		-------------------------------------



		-- fixed 2d location text:
		stex.print2d("<esc>=exit", 0.02, 0.95, 0.5 );

		stex.print2d("<spc>=alpha", 0.70, 0.95, 0.5 );


		stex.print2d("<1-5>=shuffle", 0.02, 0.02, 0.5 );

		stex.print2d("<h>=help", 0.75, 0.02, 0.5 );



		if haveSolution>0 then

			declare
				lstr: string := integer'image(haveSolution/3);
			begin
				stex.print2d("press s again to solve"&lstr, 0.30, 0.94, 0.3);
			end;

		elsif (haveSolution=0) and not failureNotice then

			stex.print2d("Timed Soln NOT found",0.3,0.94,0.3);
			failureNotice:=true;
		end if;

		if nsol>0 then
			stex.print2d("="&integer'image(nsol), 0.45, 0.02, 0.5);
		end if;




		if help then

			stex.print2d("Restore order of cubelets",         0.02, 0.85, sz,hc );
			stex.print2d("based on color or alpha-hints;",    0.02, 0.80, sz,hc );
			stex.print2d("Note position of empty space.",       0.02, 0.75, sz,hc );
			stex.print2d("1-5 keys shuffle cubelets;",        0.02, 0.70, sz,hc );
			stex.print2d("Left mouse btn rotates puzzle,",    0.02, 0.65, sz,hc );
			stex.print2d("Right mouse btn picks cubelet",     0.02, 0.60, sz,hc );
			stex.print2d("to slide into empty space.",        0.02, 0.55, sz,hc );

			stex.print2d("Laptop users: place cursor on cubelet", 0.02, 0.50, sz,hc);
			stex.print2d("then hit <enter>-key to select",        0.02, 0.45, sz,hc);

			stex.print2d("m-key to mute moves",               0.02, 0.40, sz,hc);

			stex.print2d("s-key steps towards solution,",     0.02, 0.35, sz,hc );
			stex.print2d("=-key undoes shuffle,",             0.02, 0.30, sz,hc );
			--stex.print2d("but only just after a shuffle.",    0.02, 0.30, sz,hc );

			--stex.print2d("   these Keys work too:",           0.02, 0.35, sz,hc );
			--stex.print2d("<f>=+Z   <Up>=+Y   <Lt>=-X",        0.02, 0.30, sz,hc );
			--stex.print2d("<b>=-Z   <Dn>=-Y   <Rt>=+X",        0.02, 0.25, sz,hc );

			stex.print2d("<a>=axes <c>=skinColor <s>=solve",  0.02, 0.20, sz,hc );
			stex.print2d("Zoom using mouse wheel, also...",    0.02, 0.15, sz,hc );
			stex.print2d("<i>=zoomIn  <o>=zoomOut  <r>=Restart", 0.02, 0.10, sz,hc );

		end if;

		if winner then
			stex.print2d("Correct!", 0.35, 0.92, 0.8  );


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

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

		else
			playedonce:=false;
		end if;




		if show_axes then

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


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


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


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


		end if;


		if hint then

			for row in rngs loop
			for col in rngs loop
			for lay in rngs loop
			if 
				row /= brow or col /= bcol or lay /= blay
				or (winner and origskin)
			then

				ii := indx(row,col,lay);
				pp := perm(row,col,lay);
				v4 := ( xxx(ii), yyy(ii), zzz(ii), 1.0 );
				matXvec(mvp, v4, vcc);
				stex.print3d(symbol(pp), vcc(1), vcc(2), vcc(3), vcc(4), 
				--0.9 );
				3.0/float(zoomwheel.zdist));

			end if;
			end loop;
			end loop;
			end loop;


------ begin 4jan16 addenum ----------------------------------------------
			--row1 layout:
			stex.print2d( symbol( perm(1,1,1) ), 0.90,0.90, 0.41  );
			stex.print2d( symbol( perm(1,1,2) ), 0.94,0.90, 0.41  );

			stex.print2d( symbol( perm(1,2,1) ), 0.90,0.85, 0.41  );
			stex.print2d( symbol( perm(1,2,2) ), 0.94,0.85, 0.41  );

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

			--row2 layout:
			stex.print2d( symbol( perm(2,1,1) ), 0.90,0.75, 0.41  );
			stex.print2d( symbol( perm(2,1,2) ), 0.94,0.75, 0.41  );

			stex.print2d( symbol( perm(2,2,1) ), 0.90,0.70, 0.41  );
			stex.print2d( symbol( perm(2,2,2) ), 0.94,0.70, 0.41  );

------ end 4jan16 addenum ----------------------------------------------


		end if;



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



		if dumpGLerrorQueue("mainloopend")>0 and then dbug then
			raise cube_error;
		end if;



		glflush;
		glfwSwapBuffers( mainWin );



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

	snd4ada.termSnds;

	stex.CloseFont;


	glext.binding.glDeleteProgram(programid);
	glext.binding.glDeleteBuffers(1, vertexbuff'address);
	glext.binding.glDeleteBuffers(1, colorbuff'address);
	glext.binding.glDeleteVertexArrays(1, vertexarrayid'address);


	vfree( vertices );
	vfree( colors );

	glfwdestroywindow(mainWin);
	glfwTerminate;


end seven;


