import gl4gcj.*;
import glut4gcj.*;
import java.util.Random;
import java.util.HashMap;

class Sprite extends GL
{
	private static final int MAX_SPEED = 1;
	private static final int SIZE = 32;
	private static final Random random = new Random();

	private int movableW, movableH;

	private int x, y, w, h, dx = 0, dy = 0;
	private int z_angle = 0;
	private double brightness = 0.0;

	Sprite(int screenW, int screenH)
	{
		// init size
		this.w = SIZE;
		this.h = SIZE;
		this.movableW = screenW - this.w;
		this.movableH = screenH - this.h;

		// init location
		this.x = random.nextInt(movableW);
		this.y = random.nextInt(movableH);
		this.z_angle = random.nextInt(360);
		this.brightness = random.nextDouble() / 2.0 + 0.5;

		// init speed
		while(dx == 0 && dy == 0)
		{
			dx = random.nextInt(MAX_SPEED * 2 + 1) - MAX_SPEED;
			dy = random.nextInt(MAX_SPEED * 2 + 1) - MAX_SPEED;
		}
	}

	// for Rect interface 
	public final int getX() { return this.x; }
	public final int getY() { return this.y; }
	public final int getW() { return this.w; }
	public final int getH() { return this.h; }

	public final void move()
	{
		x += dx;
		if((x < 0) || (x >= movableW))
		{
			dx = -dx;
			x += dx;
		}

		y += dy;
		if((y < 0) || (y >= movableH))
		{
			dy = -dy;
			y += dy;
		}

		z_angle = (z_angle + 4) % 360;
	}

	public final void draw()
	{
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glTranslated(this.getX() + this.getW()/2, this.getY() + this.getH()/2, 0);
		glRotated(z_angle, 0.0, 0.0, 1.0);

		int hw = this.getW()/2;
		int hh = this.getH()/2;

		glColor3d(this.brightness, this.brightness, this.brightness);

		glBegin(GL_POLYGON);
		glVertex2i(-hw,  hh);
		glVertex2i(-hw, -hh);
		glVertex2i( hw, -hh);
		glVertex2i( hw,  hh);
		glEnd();
	}
}

public class TestSpriteGL extends GLUT 
	implements DisplayFunc, IdleFunc, KeyboardFunc, ReshapeFunc, TimerFunc
{
	private static final int SCREEN_WIDTH = 640;
	private static final int SCREEN_HEIGHT = 480;
	private static final int NUMBER_OF_SPRITE = 100;

	private Sprite[] sprites;
	private long startTicks;
	private int frames = 0;

	public TestSpriteGL()
	{
		this.sprites = new Sprite[NUMBER_OF_SPRITE];
		for (int i = this.sprites.length-1;i >= 0;i--)
			this.sprites[i] = new Sprite(SCREEN_WIDTH, SCREEN_HEIGHT);
		this.startTicks = System.currentTimeMillis();
	}

	public double getFPS()
	{
		return ((double)this.frames * 1000)/ (System.currentTimeMillis() - startTicks);
	}

	public void reshape(int width, int height)
	{
		glViewport(0, 0, width, height);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0.0, 640.0, 0.0, 480.0, -10.0, 10.0);
	}

	public void display()
	{
		glClear(GL_COLOR_BUFFER_BIT);

		for (int i = sprites.length-1;i >= 0 ;i--)
		{
			this.sprites[i].draw();
		}
		glutSwapBuffers();
	}

	public void idle()
	{
		for (int i = sprites.length-1;i >= 0 ;i--)
		{
			this.sprites[i].move();
		}
		this.frames++;
		glutPostRedisplay();
	}

	public void timer(int value)
	{
		System.out.print(this.getFPS());
		System.out.println(" frames per second");

		System.out.print("Memory : ");
		System.out.print(Runtime.getRuntime().freeMemory());
		System.out.print("/");
		System.out.println(Runtime.getRuntime().totalMemory());
		
		glutTimerFunc(1000, this);
		this.frames = 0;
		this.startTicks = System.currentTimeMillis();
	}

	public void keyboard(byte key, int x, int y)
	{
		if (key == 'q' || key == 'Q')
		{
			System.out.println(this.getFPS() + " frames per second");
			System.exit(0);
		}
		else if (key == 'g' || key == 'G')
		{
			System.out.println("GC");
			System.gc();
		}
	}

	public static void main(String[] args)
	{
		
		try
		{
			glutInit(args);
			glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
//			glutInitDisplayMode(GLUT_RGB);
			glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);
			glutCreateWindow("TestSpriteGL");

			TestSpriteGL testSprite = new TestSpriteGL();
			glutDisplayFunc(testSprite);
			glutReshapeFunc(testSprite);
			glutKeyboardFunc(testSprite);
			glutIdleFunc(testSprite);
			glutTimerFunc(1000, testSprite);

			glutMainLoop();

		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
}
