/**
 * @(#) glutplane.java
 * @(#) author: Mark J. Kilgard (converted to Java by Ron Cemer)
 */

/* This program is freely distributable without licensing fees 
   and is provided without guarantee or warrantee expressed or 
   implied. This program is -not- in the public domain. */

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 glutplane extends SimpleGLAnimApplet1 
{
        /* Initialize the applet */


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


    private class planeobj
    {
        float speed = 0.0f;          /* zero speed means not flying */
        float red = 0.0f, green = 0.0f, blue = 0.0f;
        float theta = 0.0f;
        float x = 0.0f, y = 0.0f, z = 0.0f, angle = 0.0f;

        public void setColor(float r, float g, float b)
        {
            red = r;
            green = g;
            blue = b;
        }
    };


        /* Local GLAnimCanvas extension class */


    private class glutplaneCanvas extends GLAnimCanvas
        implements MouseListener, ActionListener
    {
        private final double M_PI = 3.14159265;
        private final double M_PI_2 = 1.57079632;
        private final int MAX_PLANES = 15;
        private final String ADD_PLANE = "Add plane";
        private final String REMOVE_PLANE = "Remove plane";
        private final String SUSPEND_RESUME = "Suspend/resume animation";

        private planeobj planes[] = null;
        private Random random = null;
        private PopupMenu menu = null;
        private boolean menu_showing = false;
        private boolean save_suspended = false;

        public glutplaneCanvas(int w, int h)
        {
            super(w, h);
            GLContext.gljNativeDebug = false;
            GLContext.gljClassDebug = false;
            setAnimateFps(30.0);
        }
    
        public void preInit()
        {
            doubleBuffer = true;
            stereoView = false;
        }
    
        public void init()
        {
            System.out.println("init(): " + this);
            reshape(getSize().width, getSize().height);

            gl.glClearDepth(1.0f);
            gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            gl.glMatrixMode(GL_PROJECTION);
            gl.glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 20.0f);
            gl.glMatrixMode(GL_MODELVIEW);
                /* add three initial random planes */
            planes = new planeobj[MAX_PLANES];
            for (int i = 0; i < MAX_PLANES; i++)
                planes[i] = new planeobj();
            random = new Random(System.currentTimeMillis());
            add_plane();
            add_plane();
            add_plane();

            glj.gljCheckGL();

            menu = new PopupMenu("Options");
            menu.add(ADD_PLANE);
            menu.add(REMOVE_PLANE);
            menu.add(SUSPEND_RESUME);
            menu.addActionListener(this);
            add(menu);

            addMouseListener(this);
        }
    
        public void doCleanup()
        {
            System.out.println("destroy(): " + this);
            removeMouseListener(this);
            menu.removeActionListener(this);
        }
    
        public void reshape(int width, int height)
        {
            gl.glViewport(0,0,width,height);
        }

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

            tick();

            float red, green, blue;

            gl.glClear(GL_DEPTH_BUFFER_BIT);
                /* paint black to blue smooth shaded polygon for background */
            gl.glDisable(GL_DEPTH_TEST);
            gl.glShadeModel(GL_SMOOTH);
            gl.glBegin(GL_POLYGON);
            gl.glColor3f(0.0f, 0.0f, 0.0f);
            gl.glVertex3f(-20f, 20f, -19f);
            gl.glVertex3f(20f, 20f, -19f);
            gl.glColor3f(0.0f, 0.0f, 1.0f);
            gl.glVertex3f(20f, -20f, -19f);
            gl.glVertex3f(-20f, -20f, -19f);
            gl.glEnd();
                /* paint planes */
            gl.glEnable(GL_DEPTH_TEST);
            gl.glShadeModel(GL_FLAT);
            for (int i = 0; i < MAX_PLANES; i++)
            {
                if (planes[i].speed != 0.0f)
                {
                    gl.glPushMatrix();
                    gl.glTranslatef(planes[i].x, planes[i].y, planes[i].z);
                    gl.glRotatef(290.0f, 1.0f, 0.0f, 0.0f);
                    gl.glRotatef(planes[i].angle, 0.0f, 0.0f, 1.0f);
                    gl.glScalef(1.0f / 3.0f, 1.0f / 4.0f, 1.0f / 4.0f);
                    gl.glTranslatef(0.0f, -4.0f, -1.5f);
                    gl.glBegin(GL_TRIANGLE_STRIP);
                        /* left wing */
                    gl.glVertex3f(-7.0f, 0.0f, 2.0f);
                    gl.glVertex3f(-1.0f, 0.0f, 3.0f);
                    red = planes[i].red;
                    green = planes[i].green;
                    blue = planes[i].blue;
                    gl.glColor3f(red,green,blue);
                    gl.glVertex3f(-1.0f, 7.0f, 3.0f);
                        /* left side */
                    gl.glColor3f(0.6f * red, 0.6f * green, 0.6f * blue);
                    gl.glVertex3f(0.0f, 0.0f, 0.0f);
                    gl.glVertex3f(0.0f, 8.0f, 0.0f);
                        /* right side */
                    gl.glVertex3f(1.0f, 0.0f, 3.0f);
                    gl.glVertex3f(1.0f, 7.0f, 3.0f);
                        /* final tip of right wing */
                    gl.glColor3f(red, green, blue);
                    gl.glVertex3f(7.0f, 0.0f, 2.0f);
                    gl.glEnd();
                    gl.glPopMatrix();
                }
            }

            glj.gljSwap();
            glj.gljCheckGL();
            glj.gljFree();
    
            // if (!isSuspended()) repaint();  // Animate at full speed.
        }

        // Methods required for the implementation of MouseListener
        public void mouseEntered(MouseEvent evt)
        {
        }
    
        public void mouseExited(MouseEvent evt)
        {
        }
    
        public void mousePressed(MouseEvent evt)
        {
                // If user presses right mouse button within canvas area,
                // suspend animation and pop up menu.
                // If menu was already popped up and user presses either
                // mouse button within canvas area, resume animation
                // because the menu will have been removed automatically.
            if (!menu_showing)
            {
                if ((evt.getModifiers() & evt.BUTTON3_MASK) != 0)
                {
                    menu_showing = true;
                    save_suspended = isSuspended();
                    if (!save_suspended)
                    {
                        setSuspended(true);
                        repaint(100);
                        try
                        {
                            Thread.currentThread().sleep(200);
                        }
                        catch (Exception e)
                        { }
                    }
                    menu.show(this,evt.getX(),evt.getY());
                }
                else
                {
                        // Must be left button.
                    if (isSuspended()) repaint();
                }
            }
            else
            {
                menu_showing = false;
                setSuspended(save_suspended);
            }
        }
    
        public void mouseReleased(MouseEvent evt)
        {
        }
    
        public void mouseClicked(MouseEvent evt)
        {
        }

        // Method required for the implementation of ActionListener
        public void actionPerformed(ActionEvent evt)
        {
            String c = evt.getActionCommand();
            if (c.equals(ADD_PLANE))
            {
                add_plane();
            }
            else if (c.equals(REMOVE_PLANE))
            {
                remove_plane();
            }
            else if (c.equals(SUSPEND_RESUME))
            {
                if (menu_showing)
                    save_suspended = !save_suspended;
                else
                    setSuspended(!isSuspended());   // not likely to happen
            }
            if (menu_showing)
            {
                menu_showing = false;
                setSuspended(save_suspended);
            }
        }
    
        private void tick_per_plane(int i)
        {
            float theta = planes[i].theta += planes[i].speed;
            planes[i].z = -9.0f + 4.0f * (float)Math.cos(theta);
            planes[i].x = 4 * (float)Math.sin(2.0f * theta);
            planes[i].y = (float)Math.sin(theta / 3.4f) * 3.0f;
            planes[i].angle = (float)
                (((Math.atan(2.0) + M_PI_2) * Math.sin(theta) - M_PI_2) *
                 180.0 / M_PI);
            if (planes[i].speed < 0.0f) planes[i].angle += 180.0f;
        }

        private void add_plane()
        {
            for (int i = 0; i < MAX_PLANES; i++)
            {
                if (planes[i].speed == 0.0f)
                {
                    int c = random.nextInt() & 0x07;
                    while (c == 0) c = random.nextInt() & 0x07;
                    float r = (float)((c >> 2) & 0x01);
                    float g = (float)((c >> 1) & 0x01);
                    float b = (float)(c & 0x01);
                        // Blue fades into the background; lighten it up.
                    if (c == 0x01) r = g = 0.4f;
                    planes[i].setColor(r,g,b);
                    planes[i].speed =
                        ((float)(random.nextInt() % 20)) * 0.001f + 0.02f;
                    if ((random.nextInt() & 0x01) != 0)
                        planes[i].speed *= -1.0f;
                    planes[i].theta =
                        ((float)(random.nextInt() % 257))*0.1111f;
                    tick_per_plane(i);
                    if (isSuspended()) repaint();
                    return;
                }
            }
        }

        private void remove_plane()
        {
            for (int i = MAX_PLANES - 1; i >= 0; i--)
            {
                if (planes[i].speed != 0.0f)
                {
                    planes[i].speed = 0.0f;
                    if (isSuspended()) repaint();
                    return;
                }
            }
        }

        private void tick()
        {
            for (int i = 0; i < MAX_PLANES; i++)
            {
                if (planes[i].speed != 0.0f)
                    tick_per_plane(i);
            }
        }
    }
}
