/*
 * Swogl - Swing meets OpenGL
 * 
 * Copyright 2007-2011 Marco Hutter - http://swogl.javagl.de
 */

package de.javagl.swogl.samples;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

import de.javagl.swogl.MatrixUtils;
import de.javagl.swogl.SwoglComponent;
import de.javagl.swogl.SwoglComponents;
import de.javagl.swogl.Texture;
import de.javagl.swogl.Textures;
import de.javagl.swogl.geometry.Geometries;
import de.javagl.swogl.geometry.Geometry;
import de.javagl.swogl.geometry.SimpleGeometry;

/**
 * A class containing static methods for creating the demo SwoglComponents
 */
public class SwoglDemoComponents
{
    /**
     * Creates the SwoglComponent containing the Swogl logo and
     * some simple Swing Components
     *
     * @return The logo-SwoglComponent
     */
    public static SwoglComponent createSwoglLogoComponent()
    {
        return createSwoglLogoComponent(false);
    }
    
    /**
     * Creates the SwoglComponent containing the Swogl logo and
     * some simple Swing Components
     *
     * @param animated Whether the geometry should be animated
     * @return The logo-SwoglComponent
     */
    public static SwoglComponent createSwoglLogoComponent(boolean animated)
    {
        JComponent contents = createGridPanel(10,10);
        contents.setLayout(new GridLayout(1,1));
        contents.add(createMixedComponents());
        Dimension preferredSize = new Dimension(420, 220);
        contents.setPreferredSize(preferredSize);

        contents.setName("SwoglLogoComponent contents");
        SwoglComponent swoglComponent = null;
        if (animated)
        {
            Geometry geometry = createAnimatedGeometry(
                preferredSize.width, preferredSize.height, contents);
            Texture texture = Textures.create(
                preferredSize.width, preferredSize.height);
            swoglComponent = SwoglComponents.create(
                geometry, texture, contents);
        }
        else
        {
            swoglComponent = SwoglComponents.create(contents);
        }

        // Set the transform of the SwoglComponent, describing
        // its position and orientation in space
        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(-40,0,200),
            MatrixUtils.rotateY(Math.toRadians(-25)),
            MatrixUtils.rotateX(Math.toRadians(-25)));
        swoglComponent.setTransform(transform);

        return swoglComponent;
    }


    /**
     * Creates a JPanel that paints a faked triangle mesh
     * 
     * @param gx The grid size in x-direction
     * @param gy The grid size in y-direction
     * @return A panel painting a faked triangle mesh
     */
    static JComponent createGridPanel(final int gx, final int gy)
    {
        // Create the main panel which paints the fake triangle mesh
        JPanel mainPanel = new JPanel()
        {
            private static final long serialVersionUID = -444716646299495949L;

            @Override
            public void paint(Graphics gr)
            {
                super.paint(gr);

                Graphics2D g = (Graphics2D)gr;
                g.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING, 
                    RenderingHints.VALUE_ANTIALIAS_ON);
                
                g.setPaint(new GradientPaint(
                    new Point(getWidth()/2,getHeight()/2), 
                    new Color(0,0,0,0),
                    new Point(getWidth(), getHeight()), 
                    new Color(128,128,128,255)));
                for (int x=0; x<getWidth(); x+=gx)
                {
                    g.drawLine(x, 0, x, getHeight());
                }
                for (int y=0; y<getHeight(); y+=gy)
                {
                    g.drawLine(0, y, getWidth(), y);
                }
                for (int x=-getWidth(); x<getWidth(); x+=gx)
                {
                    g.drawLine(x, 0, x+getHeight(), getHeight());
                }

            }
        };
        return mainPanel;
    }
    
    

    /**
     * Create and return a JPanel containing some mixed Swing
     * Components
     * 
     * @return A panel containing some mixed components
     */
    private static JComponent createMixedComponents()
    {
    	JPanel mainPanel = new JPanel(new BorderLayout());
    	
        // Create the panel for the NORTH of the mainPanel,
        // containing some simple Swing components
        JPanel panel = new JPanel(new GridLayout(1,0));

        // A JSpinner
        JSpinner spinner = new JSpinner();
        spinner.setValue(123);
        panel.add(spinner);

        // A JSlider
        JSlider slider = new JSlider();
        slider.setMajorTickSpacing(25);
        slider.setMinorTickSpacing(5);
        slider.setPaintTicks(true);
        panel.add(slider);

        // A JButton
        panel.add(new JButton("JButton"));

        // A JCheckBox
        panel.add(new JCheckBox("JCheckBox"));

        mainPanel.add(panel, BorderLayout.NORTH);

        // Create the label showing the Swogl logo text,
        // and place it in the CENTER of the mainPanel
        mainPanel.add(createSwoglLogo(100), BorderLayout.CENTER);

        // Create the panel for the SOUTH of the mainPanel,
        // containing some more simple Swing components
        panel = new JPanel(new GridLayout(1,0));

        // A JRadioButton
        panel.add(new JRadioButton("JRadioButton"));

        // A JTextField
        panel.add(new JTextField("JTextField"));

        // A JProgressBar
        JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        progressBar.setValue(12);
        panel.add(progressBar);

        // A JLabel. Well. That's boring, but... however.
        panel.add(new JLabel("JLabel", JLabel.CENTER));

        mainPanel.add(panel, BorderLayout.SOUTH);
        
        return mainPanel;
    }
    
    
    
    /**
     * Creates and returns a JLabel containing the Swogl logo
     * 
     * @param  fontSize The font size
     * @return A label containing the Swogl logo
     */
    private static JComponent createSwoglLogo(int fontSize)
    {
        JLabel label = new JLabel("", JLabel.CENTER);
        label.setFont(label.getFont().deriveFont(Font.PLAIN));
        label.setText(
            "<html>" +
            "<span style=\"font-size:"+(8*fontSize)+"%\">" +
            "<span style=\"color:#FF0000\">S</span>" +
            "<span style=\"color:#F70007\">w</span>" +
            "<span style=\"color:#770077\">o</span>" +
            "<span style=\"color:#0700F7\">g</span>" +
            "<span style=\"color:#0000FF\">l</span>" +
            "</span>" + 
            "<br />" +
            "<span style=\"font-size:"+fontSize+"%\">" +
            "Swing meets OpenGL" +
            "</span>" + 
            "</html>");
        return label;
    }
    
    

    
//    /**
//     * Create a SwoglComponent with the given width and height, containing
//     * a geometry with the specified number of points, which is animated
//     * with a simple sine wave pattern.
//     * 
//     * @param width The width of the component
//     * @param height The height of the component
//     * @param pointsX The number of points of the geometry
//     * @param pointsY The number of points of the geometry
//     * @return The animated SwoglComponent
//     */
//    public static SwoglComponent createAnimatedSwoglComponent(
//                    int width, int height, int pointsX, int pointsY)
//    {
//        // Create a SwogComponent with a DefaultGeometry that
//        // is distorted with a PointTransformer
//        final DefaultGeometry geometry = 
//            new DefaultGeometry(width,height,pointsX,pointsY);
//        final Vector3f offsets = new Vector3f();
//        PointTransformer pointTransformer = new PointTransformer()
//        {
//            public void transformPoint(Point3f point)
//            {
//                float rx = point.x / geometry.getWidth();
//                float ry = point.y / geometry.getHeight();
//                point.z = (float)(20 *
//                                Math.sin(offsets.x + 8*(0.5 + rx)) *
//                                Math.cos(offsets.y + 6*(0.25 + ry)));
//            }
//        };
//        geometry.setPointTransformer(pointTransformer);
//        final SwoglComponent swoglComponent = 
//            new SwoglComponent(width, height, geometry);
//
//        // Create the thread that animates the geometry
//        Thread thread = new Thread(new Runnable()
//        {
//            public void run()
//            {
//                while (true)
//                {
//                    offsets.x += 0.1f;
//                    offsets.y += 0.1f;
//                    geometry.triggerVertexUpdate();
//                    swoglComponent.repaint();
//                    try
//                    {
//                        Thread.sleep(30);
//                    }
//                    catch (InterruptedException e)
//                    {
//                        Thread.currentThread().interrupt();
//                    }
//                }
//
//            }
//        });
//        thread.start();
//        return swoglComponent;
//    }
    


    /**
     * Create a demo SwoglComponent
     *
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createTableComponent()
    {
        JComponent contents = createTable(50,5);
        contents.setPreferredSize(new Dimension(300, 300));

        contents.setName("TableComponent contents");
        SwoglComponent swoglComponent = SwoglComponents.create(contents);

        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(265, -165, -50),
            MatrixUtils.rotateY(Math.toRadians(-5)),
            MatrixUtils.rotateX(Math.toRadians(-5)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }

    /**
     * Creates and returns a ScrollPane containing a JTable
     * with the given number of rows and columns
     * 
     * @param rows The number of rows
     * @param columns The number of columns
     * @return A scrollPane containing a table
     */
    private static JComponent createTable(int rows, int columns)
    {
        Object data[][] = new Object[rows][columns];
        Object headers[] = new Object[columns];

        for (int i=0; i<columns; i++)
        {
            headers[i] = "Header "+i;
            for (int j=0; j<rows; j++)
            {
                data[j][i] = "Cell "+j+","+i;
            }
        }

        JTable table = new JTable(data, headers);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        JScrollPane scrollPane = new JScrollPane(table, 
        	JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
        	JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        return scrollPane;
    }
    

    /**
     * Create a demo SwoglComponent
     *
     * @param rows The number of rows for the table
     * @param columns The number of columns for the table
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createColoredTableComponent(int rows, int columns)
    {
        JComponent contents = createColoredTable(rows, columns);
        contents.setName("ColoredTableComponent contents");
        SwoglComponent swoglComponent = SwoglComponents.create(contents);

        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(265, -165, -50),
            MatrixUtils.rotateY(Math.toRadians(-5)),
            MatrixUtils.rotateX(Math.toRadians(-5)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }
    
    /**
     * Create a table with the given number of rows and columns, which
     * contains a CellRenderer that renders the cells with some random
     * colors
     * 
     * @param rows The number of rows
     * @param columns The number of columns
     * @return The JTable
     */
    public static JComponent createColoredTable(int rows, int columns)
    {
        // Create the table data and some random colors 
        Object data[][] = new Object[rows][columns];
        Object headers[] = new Object[columns];
        final Color colors[][] = new Color[rows][columns];
        Random random = new Random(0);
        for (int i=0; i<columns; i++)
        {
            headers[i] = "Header "+i;
            for (int j=0; j<rows; j++)
            {
                data[j][i] = "Cell "+j+","+i;
                float r = random.nextFloat();
                float g = random.nextFloat();
                float b = random.nextFloat();
                
                int ir = 255-(int)(r*32);
                int ig = 255-(int)(g*32);
                int ib = 255-(int)(b*32);
                if (r > 0.95)
                {
                    ig *= 0.5f;
                    ib *= 0.5f;
                }
                colors[j][i] = new Color(ir,ig,ib);
            }
        }
        
        // Create a TableCellRenderer that renders the cells
        // with the random background colors
        class Renderer extends DefaultTableCellRenderer 
        {
            private static final long serialVersionUID = 8647839614187137070L;

            public Renderer()
            {
                setBorder(null);
            }
            @Override
            public Component getTableCellRendererComponent(
                            JTable table, Object value,
                            boolean isSelected, boolean hasFocus,
                            int row, int column) 
            {
                Component c = super.getTableCellRendererComponent(
                    table, value, isSelected, hasFocus, row, column);
                c.setBackground(colors[row][column]);
                return c;
            }
        }

        // Create a new JTable that uses the coloring TableCellRenderer
        final TableCellRenderer renderer = new Renderer();
        JTable table = new JTable(data, headers)
        {
            private static final long serialVersionUID = 4406275980882322508L;

            @Override
            public TableCellRenderer getCellRenderer(int row, int column) 
            {
                return renderer;
            }
        };
        table.setShowGrid(false);
        JScrollPane scrollPane = new JScrollPane(table, 
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        Dimension preferredSize = table.getPreferredSize();
        int dw = scrollPane.getVerticalScrollBar().getPreferredSize().width;
        preferredSize.width += 3*dw; 
        int dh = scrollPane.getHorizontalScrollBar().getPreferredSize().height;
        preferredSize.height += 3*dh; 
        scrollPane.setPreferredSize(preferredSize);
        return scrollPane;
    }
    

    
    /**
     * Create a demo SwoglComponent
     *
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createTabbedPaneComponent()
    {
        JComponent contents = createTabbedPane();
        contents.setPreferredSize(new Dimension(200, 200));
        contents.setName("TabbedPaneComponent contents");
        SwoglComponent swoglComponent = SwoglComponents.create(contents);

        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(-20, 195, -20),
            MatrixUtils.rotateY(Math.toRadians(10)),
            MatrixUtils.rotateX(Math.toRadians(5)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }

    /**
     * Creates and returns a panel containing a JTabbedPane
     * 
     * @return A scrollPane containing a text area
     */
    private static JComponent createTabbedPane()
    {
        JPanel panel = new JPanel(new GridLayout(1,1));
        panel.setOpaque(false);
        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.addTab("First tab", createTextArea(10));
        tabbedPane.addTab("Second tab", createSplitPane());
        panel.add(tabbedPane);
        return panel;
    }
    
    

    /**
     * Create a demo SwoglComponent
     *
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createTextComponent()
    {
        JComponent contents = createTextArea(50);
        contents.setPreferredSize(new Dimension(400, 200));
        contents.setName("TextComponent contents");
        SwoglComponent swoglComponent = SwoglComponents.create(contents);

        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(-200, -195, -50),
            MatrixUtils.rotateY(Math.toRadians(10)),
            MatrixUtils.rotateX(Math.toRadians(5)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }

    /**
     * Creates and returns a ScrollPane containing a JTextAray
     * with the given number of rows
     * 
     * @param rows The number of rows
     * @return A scrollPane containing a text area
     */
    private static JComponent createTextArea(int rows)
    {
        Random random = new Random(0);
        JTextPane textPane = new JTextPane();
        StyledDocument doc = textPane.getStyledDocument();
        for (int i=0; i<rows; i++)
        {
        	StyleContext sc = StyleContext.getDefaultStyleContext();
            Style defaultStyle = sc.getStyle(StyleContext.DEFAULT_STYLE);
            Style style = doc.addStyle("s"+i, defaultStyle);
            StyleConstants.setItalic(style, random.nextBoolean());
            StyleConstants.setBold(style, random.nextBoolean());
            StyleConstants.setFontSize(style, 7 + random.nextInt(9));
            String s = "All work and no play makes Marco a dull boy\n";
            try
            {
                doc.insertString(doc.getLength(), s, style);
            }
            catch (BadLocationException ble)
            {
            	// Should never happen
                throw new AssertionError("BadLocationException");
            }
        }
        JScrollPane scrollPane = new JScrollPane(textPane, 
        	JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
        	JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        return scrollPane;
    }
    

    /**
     * Create a demo SwoglComponent
     *
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createSplitPaneComponent()
    {
        JComponent contents = createSplitPane();
        contents.setPreferredSize(new Dimension(300, 250));
        contents.setName("SplitPaneComponent contents");
        final SwoglComponent swoglComponent = SwoglComponents.create(contents);

        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(-310, 150, -100),
            MatrixUtils.rotateY(Math.toRadians(5)),
            MatrixUtils.rotateX(Math.toRadians(5)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }

    
    /**
     * Creates an returns a JSplitPane containing a JTextArea and
     * some buttons
     * 
     * @return A split pane containing some simple components
     */
    private static JComponent createSplitPane()
    {
        JTextArea textArea = new JTextArea();
        textArea.setLineWrap(true);
        textArea.setWrapStyleWord(true);
        for (int i=0; i<3; i++)
        {
            textArea.append(
                "Lorem ipsum dolor sit amet, consectetur adipisici elit, " +
                "sed eiusmod tempor incidunt ut labore et dolore magna " +
                "aliqua. Ut enim ad minim veniam, quis nostrud exercitation " +
                " ullamco laboris nisi ut aliquid ex ea commodi consequat. " +
                "Quis aute iure reprehenderit in voluptate velit esse " +
                "cillum dolore eu fugiat nulla pariatur. Excepteur sint " +
                "obcaecat cupiditat non proident, sunt in culpa qui " +
                "officia deserunt mollit anim id est laborum.\n");
        }
        JScrollPane scrollPane = new JScrollPane(textArea, 
        	JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
        	JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        JPanel p = new JPanel(new GridLayout(0,1));
        for (int i=0; i<10; i++)
        {
            if (i % 2 == 1)
            {
                p.add(new JCheckBox("Component " + i));
            }
            else
            {
                p.add(new JRadioButton("Component " + i));
            }
        }

        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
        splitPane.setLeftComponent(scrollPane);
        splitPane.setRightComponent(p);
        return splitPane;
    }
    
    

    /**
     * Create a demo SwoglComponent
     *
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createTreeComponent()
    {
        JComponent contents = createTree();
        contents.setPreferredSize(new Dimension(200, 200));
        contents.setName("TreeComponent contents");
        SwoglComponent swoglComponent = SwoglComponents.create(contents);
        
        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(300, 250, -150),
            MatrixUtils.rotateY(Math.toRadians(-5)),
            MatrixUtils.rotateX(Math.toRadians(5)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }
    
    /**
     * Creates a component containing a tree and a combo box
     * 
     * @return A sample component
     */
    private static JComponent createTree()
    {
        JPanel contents = new JPanel(new BorderLayout());
        
        JTree tree = new JTree();
        JScrollPane scrollPane = new JScrollPane(tree, 
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        tree.expandRow(3);
        tree.expandRow(2);
        tree.expandRow(1);
        contents.add(scrollPane, BorderLayout.CENTER);

        JComboBox comboBox = new JComboBox(new String[]{
            "Getting", "these", "popups", "working", 
            "took", "a",  "while ;-)"});
        contents.add(comboBox, BorderLayout.SOUTH);
        
        return contents;
    }
    

 
    
    /**
     * Create a demo SwoglComponent
     *
     * @return A demo SwoglComponent
     */
    public static SwoglComponent createDesktopComponent()
    {
        JComponent contents = createDesktop();
        contents.setPreferredSize(new Dimension(600, 600));
        contents.setName("TreeComponent contents");
        SwoglComponent swoglComponent = SwoglComponents.create(contents);
        
        Matrix4f transform = MatrixUtils.mul(
            MatrixUtils.translate(0, 50, -250),
            MatrixUtils.rotateX(Math.toRadians(-15)));
        swoglComponent.setTransform(transform);
        return swoglComponent;
    }
    
    /**
     * Creates a component containing a desktop pane and some
     * internal frames
     * 
     * @return A sample component
     */
    private static JComponent createDesktop()
    {
        JPanel contents = new JPanel(new BorderLayout());
        
        JDesktopPane deskopPane = new JDesktopPane();
        contents.add(deskopPane, BorderLayout.CENTER);

        JComponent mixed = createMixedComponents();
        mixed.setPreferredSize(new Dimension(420, 220));
        deskopPane.add(createInternalFrame(mixed, 20,20));
        deskopPane.add(createInternalFrame(createSplitPane(), 60,60));
        deskopPane.add(createInternalFrame(createTree(), 100,100));
        deskopPane.add(createInternalFrame(createTextArea(10), 140,140));
        
        return contents;
    }
    
    /**
     * Create an internal frame with the given contents at the specified
     * position
     * 
     * @param contents The contents
     * @param x The x position
     * @param y The y position
     * @return The internal frame
     */
    private static JInternalFrame createInternalFrame(
        JComponent contents, int x, int y)
    {
        JInternalFrame internalFrame = new JInternalFrame("Internal frame");
        internalFrame.add(contents);
        internalFrame.setLocation(new Point(x,y));
        internalFrame.setResizable(true);
        internalFrame.setMaximizable(true);
        internalFrame.setIconifiable(true);
        internalFrame.pack();
        internalFrame.setVisible(true);
        return internalFrame;
    }
    
    
    
    /**
     * Create a geometry that is animated with a sine wave in its own thread.
     * 
     * @param w The width of the geometry
     * @param h The height of the geometry
     * @param renderComponent The component on which a repaint 
     * should be triggered.
     * @return The animated geometry
     */
    public static Geometry createAnimatedGeometry(
        final int w, final int h, final Component renderComponent)
    {
        final SimpleGeometry geometry = Geometries.create(w, h, 20, 20);
        Thread thread = new Thread(new Runnable()
        {
            /**
             * The offsets of the sine waves
             */
            private Vector3f offsets = new Vector3f();

            @Override
            public void run()
            {
                Runnable runnable = new Runnable()
                {
                    @Override
                    public void run()
                    {
                        updateGeometry();
                    }
                };
                while (true)
                {
                    SwingUtilities.invokeLater(runnable);
                    renderComponent.repaint();
                    try
                    {
                        Thread.sleep(30);
                    }
                    catch (InterruptedException e)
                    {
                        Thread.currentThread().interrupt();
                    }
                }
            }

            /**
             * Update the geometry
             */
            private void updateGeometry()
            {
                Point3f point = new Point3f();
                float sizeZ = 20;
                float freqX = (float)(2.5 * Math.PI);
                float freqY = (float)(2.5 * Math.PI);
                for (int x = 0; x < geometry.getNumPointsX(); x++)
                {
                    for (int y = 0; y < geometry.getNumPointsY(); y++)
                    {
                        float rx = (float)x / geometry.getNumPointsX();
                        float ry = (float)y / geometry.getNumPointsY();
                        float sx = (float)Math.sin(offsets.x + freqX * rx);
                        float sy = (float)Math.cos(offsets.y + freqY * ry);
                        geometry.getVertex(x, y, point);
                        point.z = sizeZ * sx * sy;
                        geometry.setVertex(x, y, point);
                    }
                }
                offsets.x += 0.1f;
                offsets.y += 0.1f;
                geometry.setVerticesModified(true);
            }
        });
        thread.setDaemon(true);
        thread.start();
        return geometry;
    }    

}

