/* ---------------------------------------------------------------------------
                                   - Jade SDK -
                             (c) 2003 Alexis PAUTROT
                            Site Web  -  jadesdk.free.fr
                            Contact   -  jadesdk@free.fr
   --------------------------------------------------------------------------- */
package tutorial_05;

// importe package moteur
import jade.engine.*;

// importe package de chargement
import jade.loader.JadeLoader;
import jade.loader.JadeContainer;

import java.awt.event.KeyEvent;
import java.awt.Image;
import java.awt.Color;



import java.awt.Frame;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;




// classe de chargement et scène de jeu
public class MainScene extends JadeKeyListener implements JadeLoader, JadeScene
{
	// container de jeu
	JadeContainer container;

	// moteur et surface d'affichage
	JadeDisplay display;

	// fonds d'écran
	JadeTileMapBackground tileMapBackground;
	JadeTileMapBackground tileMapForeground;

	// mode d'affichage du layer (0:ALL,ALL 1:ALL,NEEDED 2:NEEDED,ALL 3:NEEDED,NEEDED 4:NONE,ALL 5:NONE,NEEDED)
	int layerDisplayMode=0;

	// chaîne maj toute les secondes avec le nombre de FPS réel et la charge CPU
	String meterString="?? FPS ~";

	// variables de calcul du FPS et du % CPU
	long lastTime=0; // dernier temps mesuré en ms
	int elapsedFrames=0; // nombre de boucles executées

	// FPS souhaité
	int fps=50;

	// nombre de balles initiale
	int ballNumber=20;

	// layer de sprites
	JadeSpriteLayer spriteLayer;

	// famille balles
	JadeFamily ballFamily;

	// touche escape stoppe l'application
	public final void onKeyReleased(KeyEvent ke)
	{
		// sort si appui de la touche échappe
		if (ke.getKeyCode()==KeyEvent.VK_ESCAPE) container.exit();

		// cycle mode d'affichage
		if (ke.getKeyCode()==KeyEvent.VK_SPACE)
		{ layerDisplayMode = (layerDisplayMode + 1) % 6; }

		// change le nb de boules
		if (ke.getKeyCode()==KeyEvent.VK_RIGHT)
			createBall();
		if (ke.getKeyCode()==KeyEvent.VK_LEFT)
			ballFamily.setSize(JadeMath.max(0,ballFamily.getSize()-1));

		// change IPS voulu
		if (ke.getKeyCode()==KeyEvent.VK_DOWN)
			{ fps--; display.setAnimationRate(fps); }
		if (ke.getKeyCode()==KeyEvent.VK_UP)
			{ fps++; display.setAnimationRate(fps); }
	}

	public final void createBall()
	{
		int tx = JadeMath.urand(20);
		int ty = JadeMath.urand(15);

		while ( tileMapBackground.values[tx+(ty*20)]!=0 )
		{
			tx = JadeMath.urand(20);
			ty = JadeMath.urand(15);
		}

		// créer sprite
		JadeSprite sprite=new JadeSprite(tx*32,ty*32,ballFamily);

		// init des anciennes coordonnées obligatoire pour éviter artifacts du premier affichage
		sprite.oldx=sprite.x;
		sprite.oldy=sprite.y;

		// génère direction et vitesse aléatoires (mais non nuls)
		sprite.data[0] = JadeMath.rand(4);
		sprite.data[1] = JadeMath.rand(4);
		if ( sprite.data[0] == 0 ) sprite.data[0]=1;
		if ( sprite.data[1] == 0 ) sprite.data[1]=1;

		// ajoute à la famille
		ballFamily.addElement(sprite);

		// choisi aléatoirement frame d'animation dans les 12 disponibles (0..11)
		sprite.setAnimationFrame(JadeMath.urand(12));

		// lance animation
		sprite.playAnimation();
	}

	// méthode de chargement et de lancement de jeu
	public final void loadAndRun(JadeContainer container)
	{
		this.container=container;

		// créer moteur d'affichage
		display = new JadeDisplay(container);

		// créer interface de chargement
		JadeLoadingInterface jli=new JadeLoadingInterface(display);

		// charge fond d'écran
		jli.setMessage("Loading background..");
		jli.initializeProgress(6500);
			JadeMediaLoader.setPath("tutorial_05/");

			{
				Image[] tiles=JadeMediaLoader.parseImage(JadeMediaLoader.loadImage("tiles_bricks.gif"),1,5);
				int[] map={3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,5,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,4,2,1,1,1,1,1,4,2,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,1,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,3,0,0,0,1,4,2,0,0,0,4,2,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,3,0,0,1,0,0,3,0,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,5,0,0,4,2,0,0,0,0,0,0,0,0,0,1,0,0,3,1,4,2,0,0,2,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,2,0,0,1,0,0,1,0,0,2,0,0,0,0,0,0,0,0,5,2,3,0,0,1,0,0,1,0,0,1,1,3,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,2,1,1,1,3,1,1,1,1,1,1,4,5,2,0,0,0,0,0,0,0,0,0,0,1,1,1,1,3,1,1,1,1,4,2,4,2,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,3,1,1,1,1,1,1,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,4,2,1,1,1,1,1,1,0,0,0,0,0,0,0,5,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,1,1,0,0,0,0,0,0,0,0,0,0,1,4,2,1,3,1,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,4,5,2,1,1,1,1,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,3,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,4,2,1,0,0,1,1,0,4,2,0,0,0,0,0,0,0,0,0,3,4,5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,3,1,1,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,2,1,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,2,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
				tileMapForeground=new JadeTileMapBackground(tiles,map,26);
			}
			jli.setProgress(1000);

			{
				Image[] tiles=JadeMediaLoader.parseImage(JadeMediaLoader.loadImage("tiles_ABCD.gif"),2,2);
				int[] map={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,1,1,0,0,0,0,4,0,0,4,0,0,0,0,1,1,0,2,4,0,1,0,0,0,2,2,2,2,2,2,2,2,0,0,0,1,0,2,4,0,0,0,0,0,0,2,4,2,2,4,2,0,0,0,0,0,0,2,4,0,0,0,0,0,0,0,2,4,4,2,0,0,0,0,0,0,0,2,4,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,2,4,4,4,0,0,3,0,0,0,0,0,0,0,0,3,0,0,4,4,2,4,0,0,0,3,3,3,0,0,0,0,0,0,3,3,3,0,0,0,2,4,0,0,0,0,3,0,0,0,0,0,0,0,0,3,0,0,0,0,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,0,1,0,0,0,2,2,2,2,2,2,2,2,0,0,0,1,0,2,4,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,2,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
				tileMapBackground=new JadeTileMapBackground(tiles,map,20);
			}
			jli.setProgress(3200);

			{
				ballFamily = new JadeFamily
				(
					new JadeAnimation
					(
						JadeMediaLoader.parseImage( JadeMediaLoader.loadImage( "ball.gif" ), 12, 1),
						new int[]{0,1,2,3,4,5,6,7,8,9,10,11},
						new int[]{0,0,0,0,0,0,0,0,0,0,0,0},
						true,false
					),
					new int[]{0,0}
				);
			}
			jli.setProgress(2300);

		// accroche clavier
		display.setKeyListener(this);

		// lance la scène
		display.start(this);
	}


	// démarrage de la scène
	public final void begin()
	{
		// initialise moteur
		display.setAnimationRate(fps);

		// créer layer de sprites
		spriteLayer=new JadeSpriteLayer
		(
			display,
			new JadeCompositeBackground ( new JadeColoredBackground(new Color(130,130,170)), tileMapBackground ),
			tileMapForeground
		);
		// change le mode d'affichage : gagne un gain de 50% par rapport à ALL,ALL
		spriteLayer.setDisplayMode(JadeSpriteLayer.NEEDED,JadeSpriteLayer.ALL);

		// ajoute familles au layer
		spriteLayer.families.addElement ( ballFamily );

		// vide famille
		ballFamily.removeAllElementsFast();

		// ajoute boules à des positions aléatoires
		for (int i =0; i<ballNumber; i++) createBall();

		spriteLayer.drawAll();
		display.swapBuffers();

		lastTime = System.currentTimeMillis();
	}

	// fin de la scène
	public final void end() {}

	public final int computeEdgeFlag(int x, int y, int edge)
	{
		int mapValue=0;
		// détermine présence de murs au quatre cotés (haut,bas,gauche,droite) de la balle
		if ( (mapValue=tileMapBackground.getValueAtCoordinate(x,y)) != 0 )
		{
			// change la brique (cycle de A à D)
			tileMapBackground.setValueAtCoordinate(x,y,(mapValue%4)+1);

			// affiche la brique si affichage en mode effacement optimisé
			if ( layerDisplayMode > 1 )
			{
				// affiche brique
				tileMapBackground.drawTileAtCoordinate(x,y,display.getDoubleBufferedGraphics());

				// repeint décor au dessus
				tileMapForeground.clearRegion
				(
					tileMapBackground.getColumnAtPixel(x)*tileMapBackground.tileWidth,
					tileMapBackground.getRowAtPixel(y)*tileMapBackground.tileHeight,
					tileMapBackground.tileWidth,
					tileMapBackground.tileHeight,
					display.getDoubleBufferedGraphics()
				);
			}

			return edge;
		}
		else
			return 0;
	}

	// boucle d'animation
	public final void mainLoop()
	{
		callKeyListener();

		JadeSprite enumerated;
		JadeSpriteEnumerator enumerator = new JadeSpriteEnumerator();

		enumerator.enumerate(ballFamily);
		while ((enumerated=enumerator.getNext())!=null)
		{
			// drapeau binaire des cotés de rebond
			int edgeFlag=0;
			int wdiv2=enumerated.width>>1;
			int x1=enumerated.x+wdiv2, y1=enumerated.y;
			int x2=enumerated.x+enumerated.width, y2=enumerated.y+wdiv2;
			int x3=enumerated.x+wdiv2, y3=enumerated.y+enumerated.height;
			int x4=enumerated.x, y4=enumerated.y+wdiv2;

			// détermine présence de murs au quatre cotés (haut,bas,gauche,droite) de la balle
			// et chnage cycliquement les briques si rebond
			edgeFlag+=computeEdgeFlag(x1,y1,1);
			edgeFlag+=computeEdgeFlag(x2,y2,2);
			edgeFlag+=computeEdgeFlag(x3,y3,4);
			edgeFlag+=computeEdgeFlag(x4,y4,8);

			// rebond balle selon cotés touchés
			switch (edgeFlag)
			{
				case 0:
				case 15:
				break;

				case 2:
					if (enumerated.data[0]>0) enumerated.data[0]=-enumerated.data[0];
				break;
				case 8:
					if (enumerated.data[0]<0) enumerated.data[0]=-enumerated.data[0];
				break;

				case 4:
					if (enumerated.data[1]>0) enumerated.data[1]=-enumerated.data[1];
				break;
				case 1:
					if (enumerated.data[1]<0) enumerated.data[1]=-enumerated.data[1];
				break;

				default:
					enumerated.data[0]=-enumerated.data[0];
					enumerated.data[1]=-enumerated.data[1];
				break;
			}

			// déplace
			enumerated.moveOf(enumerated.data[0],enumerated.data[1]);

			// borne coté écran (rebond sur coté)
			if ( (enumerated.x<0) || (enumerated.x>640-enumerated.width) ) enumerated.data[0]=-enumerated.data[0];
			if ( (enumerated.y<0) || (enumerated.y>480-enumerated.height) ) enumerated.data[1]=-enumerated.data[1];
		}

		draw();
	}

	// affichage
	public final void draw()
	{
		// détermine mode d'affichage
		String message;
		switch ( layerDisplayMode )
		{
			case 0: message = "all,all"; spriteLayer.setDisplayMode(JadeSpriteLayer.ALL,JadeSpriteLayer.ALL); break;
			case 1: message = "all,needed"; spriteLayer.setDisplayMode(JadeSpriteLayer.ALL,JadeSpriteLayer.NEEDED); break;
			case 2: message = "needed,all"; spriteLayer.setDisplayMode(JadeSpriteLayer.NEEDED,JadeSpriteLayer.ALL); break;
			case 3: message = "needed,needed"; spriteLayer.setDisplayMode(JadeSpriteLayer.NEEDED,JadeSpriteLayer.NEEDED); break;
			case 4: message = "none,all"; spriteLayer.setDisplayMode(JadeSpriteLayer.NONE,JadeSpriteLayer.ALL); break;
			case 5: message = "none,needed"; spriteLayer.setDisplayMode(JadeSpriteLayer.NONE,JadeSpriteLayer.NEEDED); break;
			default : message = "";
		}
		message = meterString + "space to cycle mode (" + message + ") ~ up/down for FPS ("+fps+") ~ left/right for ball number ("+ballFamily.getSize()+") ~ esc to quit";

		// affiche le layer de sprite avec son fond d'écran
		spriteLayer.animateAndDraw();

		// affiche texte
		display.getDoubleBufferedGraphics().setColor(Color.black);
		display.getDoubleBufferedGraphics().fillRect(0,0,640,15);
		display.getDoubleBufferedGraphics().setColor(Color.white);
		display.getDoubleBufferedGraphics().drawString(message, 5,12 );

		// calcul FPS
		if ( ++elapsedFrames == JadeMath.max(10,fps/20) )
		{
			// calcul FPS
			long actualTime = System.currentTimeMillis();
			meterString = (int)((JadeMath.max(10,fps/20)*1000)/(actualTime-lastTime))+" FPS ~ ";
			lastTime = actualTime;
			elapsedFrames = 0;
		}
	}
}
