/**
 * @(#) star.java
 * @(#) author: Silicon Graphics (converted to Java by Ron Cemer)
 */

/*
 * Copyright (c) 1991, 1992, 1993 Silicon Graphics, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the name of
 * Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Silicon Graphics.
 *
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF
 * ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.util.*;
import java.io.*;
import java.util.*;
import gl4java.GLContext;
import gl4java.awt.GLAnimCanvas;
import gl4java.applet.SimpleGLAnimApplet1;

public class star extends SimpleGLAnimApplet1 
{
        /* Initialize the applet */


	public void init()
	{
	super.init();
        Dimension d = getSize();
        canvas = new starCanvas(d.width, d.height);
        add("Center", canvas);
	}


        /* Local GLAnimCanvas extension class */


    private class starCanvas extends GLAnimCanvas implements MouseListener
    {
        private static final float M_PI = 3.14159265359f;
        private static final int NORMAL = 0, WEIRD = 1;
        private static final int STREAK = 0, CIRCLE = 1;
        private static final int MAXSTARS = 400;
        private static final int MAXPOS = 10000;
        private static final int MAXWARP = 10;
        private static final int MAXANGLES = 6000;
        private int windW, windH;
        private int flag = NORMAL;
        private int starCount = MAXSTARS / 2;
        private float speed = 1.0f;
        private int nitro = 0;
        private starRec stars[];
        private float sinTable[];
        private Random rand = null;

        private class starRec
        {
            protected int type;
            protected float x[], y[], z[];
            protected float offsetX, offsetY, offsetR, rotation;

            public starRec()
            {
                x = new float[2];
                y = new float[2];
                z = new float[2];
            }
        }

        public starCanvas(int w, int h)
        {
            super(w, h);
            GLContext.gljNativeDebug = false;
            GLContext.gljClassDebug = false;
            setAnimateFps(30.0);

            stars = new starRec[MAXSTARS];
            sinTable = new float[MAXANGLES];
        }
    
        public void preInit()
        {
            doubleBuffer = true;
            stereoView = false;
        }
    
        public void init()
        {
            System.out.println("init(): " + this);
            reshape(getSize().width, getSize().height);

            Date dt = new Date();
            rand = new Random(dt.getTime());

            int n;
            for (n = 0; n < MAXSTARS; n++)
                NewStar(n, 100);
            float angle = 0.0f;
            for (n = 0; n < MAXANGLES ; n++)
            {
                sinTable[n] = (float)Math.sin(angle);
                angle += M_PI / (MAXANGLES / 2.0f);
            }
            gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            gl.glDisable(GL_DITHER);

            glj.gljCheckGL();

            addMouseListener(this);
        }
    
        public void doCleanup()
        {
            System.out.println("destroy(): " + this);
            removeMouseListener(this);
        }
    
        public void reshape(int width, int height)
        {
            windW = width;
            windH = height;

            gl.glViewport(0,0,width,height);
            gl.glMatrixMode(GL_PROJECTION);
            gl.glLoadIdentity();
            glu.gluOrtho2D(-0.5,(double)width+0.5,-0.5,(double)height+0.5);
            gl.glMatrixMode(GL_MODELVIEW);
        }

        public void display()
        {
            if (glj.gljMakeCurrent() == false) return;

            MoveStars();
            ShowStars();
            if (nitro > 0)
            {
                speed = (float)(nitro / 10) + 1.0f;
                if (speed > MAXWARP) speed = MAXWARP;
                if (++nitro > MAXWARP*10) nitro = -nitro;
            }
            else if (nitro < 0)
            {
                nitro++;
                speed = (float)(-nitro / 10) + 1.0f;
                if (speed > MAXWARP) speed = MAXWARP;
            }
        
            glj.gljSwap();
            glj.gljCheckGL();
            glj.gljFree();
    
            //if (!isSuspended()) repaint();  // Animate at full speed.
        }

        private float Sin(float angle)
        {
            int ang;
            ang = (int)angle;
            while (ang < 0) ang += MAXANGLES;
            while (ang >= MAXANGLES) ang -= MAXANGLES;
            return (sinTable[ang]);
        }
        
        private float Cos(float angle)
        {
            return Sin(angle+(((float)MAXANGLES)*0.25f));
        }

        private void NewStar(int n, int d)
        {
            stars[n] = new starRec();
            if (rand.nextInt() % 4 == 0)
                stars[n].type = CIRCLE;
            else
                stars[n].type = STREAK;
            stars[n].x[0] = (float)(rand.nextInt() % MAXPOS - MAXPOS / 2);
            stars[n].y[0] = (float)(rand.nextInt() % MAXPOS - MAXPOS / 2);
            stars[n].z[0] = (float)(rand.nextInt() % MAXPOS + d);
            if ( (rand.nextInt() % 4 == 0) && (flag == WEIRD) )
            {
                stars[n].offsetX = (float)(rand.nextInt() % 100 - 100 / 2);
                stars[n].offsetY = (float)(rand.nextInt() % 100 - 100 / 2);
                stars[n].offsetR = (float)(rand.nextInt() % 25 - 25 / 2);
            }
            else
            {
                stars[n].offsetX = 0.0f;
                stars[n].offsetY = 0.0f;
                stars[n].offsetR = 0.0f;
            }
        }

        private void RotatePoint(float[] x, float[] y, float rotation)
        {
            float tmpX, tmpY;
        
            tmpX = x[0] * Cos(rotation) - y[0] * Sin(rotation);
            tmpY = y[0] * Cos(rotation) + x[0] * Sin(rotation);
            x[0] = tmpX;
            y[0] = tmpY;
        }

        private void MoveStars()
        {
            float offset;
            int n;
        
            offset = speed * 60.0f;
        
            for (n = 0; n < starCount; n++)
            {
                stars[n].x[1] = stars[n].x[0];
                stars[n].y[1] = stars[n].y[0];
                stars[n].z[1] = stars[n].z[0];
                stars[n].x[0] += stars[n].offsetX;
                stars[n].y[0] += stars[n].offsetY;
                stars[n].z[0] -= offset;
                int rot = (int)(stars[n].rotation = stars[n].offsetR);
                while (rot < 0) rot += MAXANGLES;
                while (rot >= MAXANGLES) rot -= MAXANGLES;
                stars[n].rotation = (float)rot;
            }
        }

        private boolean StarPoint(int n)
        {
            float x0, y0, x1, y1, width;
            float xbuf[] = new float[1], ybuf[] = new float[1];
            int i;
        
            x0 = stars[n].x[0] * windW / stars[n].z[0];
            y0 = stars[n].y[0] * windH / stars[n].z[0];
            xbuf[0] = x0;
            ybuf[0] = y0;
            RotatePoint(xbuf, ybuf, stars[n].rotation);
            x0 = xbuf[0];
            y0 = ybuf[0];
            x0 += windW / 2.0f;
            y0 += windH / 2.0f;
        
            if ( (x0 >= 0.0f) && (x0 < windW) && (y0 >= 0.0f) && (y0 < windH) )
            {
                if (stars[n].type == STREAK)
                {
                    x1 = stars[n].x[1] * windW / stars[n].z[1];
                    y1 = stars[n].y[1] * windH / stars[n].z[1];
                    xbuf[0] = x1;
                    ybuf[0] = y1;
                    RotatePoint(xbuf, ybuf, stars[n].rotation);
                    x1 = xbuf[0];
                    y1 = ybuf[0];
                    x1 += windW / 2.0f;
                    y1 += windH / 2.0f;
            
                    gl.glLineWidth(MAXPOS/100.0f/stars[n].z[0]+1.0f);
                    gl.glColor3f(1.0f, (MAXWARP-speed)/MAXWARP, (MAXWARP-speed)/MAXWARP);
                    if (Math.abs(x0-x1) < 1.0f && Math.abs(y0-y1) < 1.0f)
                    {
                        gl.glBegin(GL_POINTS);
                        gl.glVertex2f(x0, y0);
                        gl.glEnd();
                    }
                    else
                    {
                        gl.glBegin(GL_LINES);
                        gl.glVertex2f(x0, y0);
                        gl.glVertex2f(x1, y1);
                        gl.glEnd();
                    }
                }
                else
                {
                    width = MAXPOS / 10.0f / stars[n].z[0] + 1.0f;
                    gl.glColor3f(1.0f, 0.0f, 0.0f);
                    gl.glBegin(GL_POLYGON);
                    for (i = 0; i < 8; i++)
                    {
                        float x = x0 + width * Cos((float)i*MAXANGLES/8.0f);
                        float y = y0 + width * Sin((float)i*MAXANGLES/8.0f);
                        gl.glVertex2f(x, y);
                    };
                    gl.glEnd();
                }

                return true;
            }
            return false;
        }

        private void ShowStars()
        {
            gl.glClear(GL_COLOR_BUFFER_BIT);
            for (int n = 0; n < starCount; n++)
            {
                if (   (stars[n].z[0] > speed)
                    || ( (stars[n].z[0] > 0.0f) && (speed < MAXWARP) )   )
                {
                    if (!StarPoint(n))
                        NewStar(n, MAXPOS);
                }
                else
                    NewStar(n, MAXPOS);
            }
        }

        // Methods required for the implementation of MouseListener
        public void mouseEntered( MouseEvent evt )
        {
        }
    
        public void mouseExited( MouseEvent evt )
        {
        }
    
        public void mousePressed( MouseEvent evt )
        {
            flag = (flag == NORMAL) ? WEIRD : NORMAL;
        }
    
        public void mouseReleased( MouseEvent evt )
        {
        }
    
        public void mouseClicked( MouseEvent evt )
        {
        }
    }
}
