--
-- Copyright (C) 2021  <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 ada.numerics.float_random;
with snd4ada;
with player;
with globals;

package body aliens is

	G : Ada.Numerics.Float_Random.Generator;



procedure aliensReset is
	k: integer;
begin
	--aliensClear(aliens.posX,aliens.posY, aliens.right,aliens.bottom);

	aliens.posX:=0;
	aliens.posY:=0;
	aliens.right:=0;
	aliens.bottom:=0;
	aliens.left:=0;
	aliens.speed:=1;

	--initialize alien matrix
	for j in 0..aliens_max_number_x-1 loop -- 0..9
		for i in 0..aliens_max_number_y-1 loop -- 0..4
		if i>=3 then k:=3;
		elsif i>=1 then k:=2;
		else k:=1; end if;
			alienBlock(i,j):=k;
		end loop;
	end loop;

	--reset missiles
	for i in 0..aliens_max_missiles-1 loop
		alienshotx(i):=0;
		alienshoty(i):=0;
	end loop;

	alienshotx(0):=5; --x pos of 1st alienshot
	alienshoty(0):=1; --y pos of 1st alienshot
			
end aliensReset;




procedure bunkersReset is
	bunkerd: array(0..3,0..79) of character :=
	(
		"        ###                 ###                 ###                 ###         ",
		"       #####               #####               #####               #####        ",
		"      #######             #######             #######             #######       ",
		"      ##   ##             ##   ##             ##   ##             ##   ##       "
	);
--     12345678901234567890123456789012345678901234567890123456789012345678901234567890
--     80 characters wide

begin
	for a in 0..79 loop
	for b in 0..3 loop
		if bunkerd(b,a)='#' then
			bunker(b,a):=1;
		else
			bunker(b,a):=0;
		end if;
	end loop;
	end loop;

end bunkersReset;


function aliensMove return integer is
use globals;
	cx,cy, fReachedPlayer: integer := 0;
begin
	analyze;
	aliens.posX := aliens.posX + aliens.speed;

	vad := (vad+1) mod 4;
	if vad=0 then snd4ada.playSnd(globals.vad1);
	elsif vad=1 then snd4ada.playSnd(globals.vad2);
	elsif vad=2 then snd4ada.playSnd(globals.vad3);
	elsif vad=3 then snd4ada.playSnd(globals.vad4);
	else raise program_error; end if;


	if aliens.posX = (globals.BUNKERWIDTH + globals.BUNKERX - 5 - aliens.right) 
	or aliens.posX = (globals.BUNKERX + aliens.left) then
		
		-- move aliens downwards
		aliens.posY := aliens.posY + 1;

		--fastrgv: speedup aliens as they descend:
		globals.weite := globals.weite - globals.deltaWeite; --begins @ weiteHi

		--limit speedup of alien moves:
		if globals.weite < globals.weiteLo then 
			globals.weite:=globals.weiteLo; 
		end if;
	

		-- aliens reached player [ ab in {0,2,4,6,8}, depending on #shot ]
		if (aliens.posY = globals.SCREENHEIGHT - 2 - aliens.bottom) then --apy=24-2-ab
			fReachedPlayer := 1;		-- set return value
		end if;
		
		-- aliens reached bunkers //funzt nicht ganz: todo
		if (aliens.posY = globals.BUNKERY - aliens.bottom) then --apy=16-ab
			-- clear bunkers
			for cx in 0..79 loop
			for cy in 0..3 loop
					bunker(cy,cx):=0;	
				end loop;
			end loop;
		end if;
		
		aliens.speed := aliens.speed * (-1);	  -- change direction of aliens' movements
	end if;


	return fReachedPlayer;				  -- return if aliens reached player

end aliensMove;


procedure analyze is
use globals;
	k,row,c : integer := 0;
begin
	aliens.left:=1;
	aliens.right:=-1;
	aliens.bottom:=-1;
	shipnum:=0;
	for k in 0..aliens_max_number_x-1 loop
		lowest_ship(k):=-1;
	end loop;

	for row in 0..((aliens_max_number_y*2)-1) loop -- row in 0..9  [ 0..(5*2-1) ]
		if row mod 2 = 0 then

			myassert(c>=0, 301);
			myassert(c<aliens_max_number_y, 302);
			for k in 0..aliens_max_number_x-1 loop -- k in 0..9
				if alienBlock(c,k) /=0 then
					lowest_ship(k):=row; -- screen vert.dist from top row of aliens
					shipnum:=shipnum+1;

					if aliens.left  = 1 or -k>aliens.left   then aliens.left:=-k; end if;
					if aliens.right =-1 or  k>aliens.right  then aliens.right:=k; end if;
					if aliens.bottom=-1 or  c>aliens.bottom then aliens.bottom:=c; end if;

				end if;
			end loop;

		else
			c:=c+1;
		end if;
	end loop;

	aliens.bottom:=aliens.bottom*2; -- every odd row is empty
	aliens.left:=aliens.left*3; -- aliens are 3 chars wide
	aliens.right:=aliens.right*3;


end analyze;



-- move aliens' missiles and do player/bunker hit testing
-- a zero value in alienshotx indicates that the appropriate missile is loaded, 
-- but not fired
function aliensMissileMove return integer is
use globals;

	ret,i,tmp,fPlayerWasHit : integer := 0;
	rand01: float;
begin

	-- calculate threshold when next missile should be fired
	-- it is done here to save calculation time in for-instruction 
	--shootThreshold := (globals.skill_level*8) * (shipnum+2);

	--decrease for more alien missiles...# tics between alien missiles
	shootThreshold := globals.shootHi - globals.level*globals.shootInc; -- 22sep21
	if shootThreshold<globals.shootLo then 
		shootThreshold:=globals.shootLo; 
	end if; --lowerBound
	--myassert(shootThreshold mod globals.skip = 0, 990);

	alienshot_counter := alienshot_counter + alienshotInc; -- + 10

	for i in 0..aliens_max_missiles-1 loop

		if alienshotx(i) /=0 then -- missile is flying

			ret := bunkersHitCheck(alienshotx(i),alienshoty(i));
			if ret = 1 then -- missile hit bunkers
				alienshotx(i):=0; -- zero reloads missile
			end if;
			alienshoty(i) := alienshoty(i)+1; -- move missile downwards
			ret := player.playerHitCheck( alienshotx(i), alienshoty(i) );
			if ret = 1 then --player was hit by alien missile
				alienshotx(i):=0; -- reload missile
				fPlayerWasHit := 1;
			end if;

		else -- missile not launched yet

			-- start new missile if counter says so
			-- 		only shoot if there is an alien left
			if alienshot_counter > shootThreshold and shipnum>0 then
				alienshot_counter := 0;

				loop
					rand01 := Ada.Numerics.Float_Random.Random(G); -- [0..1]
					tmp := integer( rand01 * float(aliens_max_number_x-1)); 
					myassert(tmp>=0, 201);
					myassert(tmp<aliens_max_number_x, 202);
					-- tmp: Col to launch missile
					exit when lowest_ship(tmp) /= -1;
				end loop;

				alienshoty(i):=aliens.posY+lowest_ship(tmp);
				alienshotx(i):=aliens.posX+tmp*3; --set x pos of missile
			end if;

		end if;

		-- display missiles if still running or just launched; 
		-- could have been modified in the above code
		if alienshotx(i) /=0 then
			if alienshoty(i) = globals.screenheight -1 then --missile went off screen
				alienshotx(i) := 0; --reload missile
			end if;
		end if;

	end loop; -- for i

	return fPlayerWasHit;

end aliensMissileMove;


function aliensHitCheck( shotx, shoty: integer ) return integer is
use globals;

	alienType, shipx, shipy: integer := 0;
	xok,yok: boolean := false;

begin

	--check if missile is within alien-rectangle
	if  (shotx >= aliens.posX) 
	and (shotx <= aliens.posX + aliens_max_number_x*3 - 1)

	and (shoty >= aliens.posY)
	and (shoty <= aliens.posY + (aliens_max_number_y - 1) * 2 ) then

		--calculate which ship was hit
		shipx := (shotx - aliens.posX) / 3;
		shipy := (shoty - aliens.posY) / 2;
		myassert( shipy>=0, 11);
		myassert( shipy<aliens_max_number_y, 12);
		myassert( shipx>=0, 13);
		myassert( shipx<aliens_max_number_x, 14);
		alienType := alienBlock(shipy,shipx);
		if alienType /= 0 then
			alienBlock(shipy,shipx) := 0; --delete alien ship
			snd4ada.playSnd(globals.mykill);
		end if;
	
	else
		globals.missedShots := globals.missedShots+1;
		finalshotx:=shotx;
		finalshoty:=shoty;
		finalaposx:=aliens.posx;
		finalaposy:=aliens.posy;
	end if;

	return alienType;

end aliensHitCheck;


function bunkersHitCheck(shotx, shoty: integer) return integer is
	adjx, adjy, fBunkerWasHit : integer := 0;
begin

	--check if missile is within bunker-rectangle
	if  (shotx >= globals.bunkerx)
	and (shotx < globals.bunkerx + globals.bunkerwidth)
	and (shoty >= globals.bunkery)
	and (shoty < globals.bunkery + globals.bunkerheight) then
		adjy := shoty - globals.bunkery;
		adjx := shotx - globals.bunkerx;
		if bunker(adjy,adjx) = 1 then --still an element
			bunker(adjy,adjx) := 0; --delete bunker element
			fBunkerWasHit := 1;
		end if;
	end if;

	return fBunkerWasHit;

end bunkersHitCheck;

end aliens;
