import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import com.jogamp.opengl.*;
import com.jogamp.opengl.awt.*;

/**
 *  A viewer for the polyhedral models defined in Polyhedron.java.
 *  The user can select the model and can control some aspects of the
 *  display.  If a model does not already have colors for its faces,
 *  then random colors are assigned.  The user can drag the polyhedron
 *  to rotate the view.
 */
public class IFSPolyhedronViewer extends GLJPanel implements GLEventListener {

    /**
     * A main routine to create and show a window that contains a
     * panel of type IFSPolyhedronViewer.  The program ends when the
     * user closes the window.
     */
    public static void main(String[] args) {
        JFrame window = new JFrame("IFS Polyhedron Viewer -- ROTATE WITH MOUSE");
        IFSPolyhedronViewer panel = new IFSPolyhedronViewer();
        window.setContentPane(panel);
        window.setJMenuBar(panel.createMenuBar());
        window.pack();
        window.setLocation(50,50);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.setVisible(true);
        panel.requestFocusInWindow();
    }

    /**
     * Constructor for class UnlitCube.
     */
    public IFSPolyhedronViewer() {
        super( new GLCapabilities(null) ); // Makes a panel with default OpenGL "capabilities".
        setPreferredSize( new Dimension(500,500) );
        addGLEventListener(this);
    }

    //-------------------- methods to draw the cube ----------------------

    private Camera camera;
    private Polyhedron currentModel;

    private JRadioButtonMenuItem orthographic, drawEdges, drawFaces, drawBoth, coloredFaces;

    //-------------------- GLEventListener Methods -------------------------

    /**
     * Draw the current model, with display options determined by the radio button menu items.
     */
    public void display(GLAutoDrawable drawable) {    

        GL2 gl2 = drawable.getGL().getGL2(); // The object that contains all the OpenGL methods.

        gl2.glClear( GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT );

        if (currentModel.faceColors == null) {
            // Make up random face colors.
            currentModel.faceColors = new double[currentModel.faces.length][];
            for (int i = 0; i < currentModel.faceColors.length; i++) {
                double[] rgb = { Math.random(), Math.random(), Math.random() };
                currentModel.faceColors[i] = rgb;
            }
        }

        camera.setOrthographic(orthographic.isSelected());
        camera.apply(gl2);
        gl2.glPushMatrix();
        double scale = 1.0/currentModel.maxVertexLength;
        gl2.glScaled(scale,scale,scale);  // scale to fit nicely in window
        boolean colored = coloredFaces.isSelected();
        int i,j;
        if (drawFaces.isSelected() || drawBoth.isSelected()) {
            if (drawBoth.isSelected()) {
                gl2.glEnable(GL2.GL_POLYGON_OFFSET_FILL);
            }
            gl2.glColor3f(1,1,1); // in case colored is false
            for (i = 0; i < currentModel.faces.length; i++) {
                if (colored) {
                    gl2.glColor3dv( currentModel.faceColors[i], 0 );
                }
                gl2.glBegin(GL2.GL_TRIANGLE_FAN);
                for (j = 0; j < currentModel.faces[i].length; j++) {
                    int vertexNum = currentModel.faces[i][j];
                    gl2.glVertex3dv( currentModel.vertices[vertexNum], 0 );
                }
                gl2.glEnd();
            }
            gl2.glDisable(GL2.GL_POLYGON_OFFSET_FILL);
        }
        if (drawEdges.isSelected() || drawBoth.isSelected()) {
            if (drawBoth.isSelected()) {
                gl2.glColor3f(0,0,0);
            }
            else {
                gl2.glColor3f(1,1,1);
            }
            for (i = 0; i < currentModel.faces.length; i++) {
                gl2.glBegin(GL2.GL_LINE_LOOP);
                for (j = 0; j < currentModel.faces[i].length; j++) {
                    int vertexNum = currentModel.faces[i][j];
                    gl2.glVertex3dv( currentModel.vertices[vertexNum], 0 );
                }
                gl2.glEnd();
            }
        }
        gl2.glPopMatrix();

    } // end display()

    public void init(GLAutoDrawable drawable) {
        // called when the panel is created
        GL2 gl2 = drawable.getGL().getGL2();
        gl2.glClearColor( 0, 0, 0, 1 );
        gl2.glEnable(GL2.GL_DEPTH_TEST);
        gl2.glLineWidth(2);
        gl2.glPolygonOffset(1,2);
        camera = new Camera();
        camera.lookAt(2,2,6, 0,0,0, 0,1,0);
        camera.setScale(1.2);
        camera.installTrackball(this);
        currentModel = Polyhedron.stellatedDodecahedron;
    }

    public void dispose(GLAutoDrawable drawable) {
        // called when the panel is being disposed
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        // called when user resizes the window
    }

    // ---------------------- Menu bar ------------------------------------------------

    private JMenuBar createMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        JMenu model = new JMenu("Polyhedron");
        JMenu render = new JMenu("Render Options");
        menuBar.add(model);
        menuBar.add(render);
        
        ActionListener repainter = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                repaint();
            }
        };

        JRadioButtonMenuItem[] items;
        items = createRadioMenuGroup(new String[] {"Colored Faces", "WhiteFaces"}, render, repainter);
        coloredFaces = items[0];
        coloredFaces.setSelected(true);
        render.addSeparator();
        items = createRadioMenuGroup(new String[] {"Draw Faces Only", "Draw Edges Only", "Draw Both"}, 
                                                            render, repainter);
        drawFaces = items[0];
        drawEdges = items[1];
        drawBoth = items[2];
        drawBoth.setSelected(true);
        render.addSeparator();
        items = createRadioMenuGroup(new String[] {"Perspective Projetion", "Orthographics Projection"}, render, repainter);
        orthographic = items[1];
        items[0].setSelected(true);

        items = createRadioMenuGroup(new String[] {
                "House",
                "Cube",
                "Dodecahedron",
                "Icosahedron",
                "Octahedron",
                "Rhombic Dodecahedron",
                "Socer Ball",
                "Stellated Dodecahedron",
                "Stellated Icosahedron",
                "Stellated Octahedron",
                "Tetrahedron",
                "Truncated Icosahedron",
                "Truncated Rhombic Dodecahedron"
        }, 
        model,
        new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                switch (evt.getActionCommand()) {
                case "House": currentModel = Polyhedron.house; break;
                case "Cube": currentModel = Polyhedron.cube; break;
                case "Dodecahedron": currentModel = Polyhedron.dodecahedron; break;
                case "Icosahedron": currentModel = Polyhedron.icosahedron; break;
                case "Octahedron": currentModel = Polyhedron.octahedron; break;
                case "Rhombic Dodecahedron": currentModel = Polyhedron.rhombicDodecahedron; break;
                case "Socer Ball": currentModel = Polyhedron.socerBall; break;
                case "Stellated Dodecahedron": currentModel = Polyhedron.stellatedDodecahedron; break;
                case "Stellated Icosahedron": currentModel = Polyhedron.stellatedIcosahedron; break;
                case "Stellated Octahedron": currentModel = Polyhedron.stellatedOctahedron; break;
                case "Tetrahedron": currentModel = Polyhedron.tetrahedron; break;
                case "Truncated Icosahedron": currentModel = Polyhedron.truncatedIcosahedron; break;
                case "Truncated Rhombic Dodecahedron": currentModel = Polyhedron.truncatedRhombicDodecahedron; break;
                }
                camera.lookAt(2,2,6, 0,0,0, 0,1,0);
                repaint();
            }
        });
        items[7].setSelected(true);

        return menuBar;
    }

    private JRadioButtonMenuItem[] createRadioMenuGroup(String[] itemNames, JMenu menu, ActionListener listener) {
        JRadioButtonMenuItem[] items = new JRadioButtonMenuItem[itemNames.length];
        ButtonGroup group = new ButtonGroup();
        for (int i = 0; i < itemNames.length; i++) {
            JRadioButtonMenuItem item = new JRadioButtonMenuItem(itemNames[i]);
            group.add(item);
            items[i] = item;
            menu.add(item);
            if (listener != null) {
                item.addActionListener(listener);
            }
        }
        return items;
    }

}
