/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 */

import com.sun.glass.ui.Application;
import com.sun.glass.ui.Launchable;
import com.sun.glass.ui.Screen;
import com.sun.glass.ui.Window;
import com.sun.glass.ui.View;
import com.sun.glass.ui.Pen;
import com.sun.glass.ui.MenuBar;
import com.sun.glass.ui.Pixels;
import com.sun.glass.ui.Timer;

import com.sun.glass.events.ViewEvent;
import com.sun.glass.events.WindowEvent;

import com.sun.rain.graphics.Color;
import com.sun.rain.graphics.Graphics2D;
import com.sun.rain.graphics.Graphics3D;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Noise implements Launchable
{
    static final boolean IS3D = true;
    static final boolean ANIMATE = true;
    static final boolean USE_3RD_THREAD = true;
    static final boolean REUSE_PIXELS = false; // keep disabled until RT-20612
    static final int WIDTH = 600;
    static final int HEIGHT = 256;
    static final int FPS = 30;
    static final int DELAY = 0; // in milliseconds, artificial delay to simulate complex drawing
    static final String TITLE = "Glass Noise";
    
    static Noise.Plasma plasma = null;
    
    public static void main(String args[])
    {
        Application.Run(args, new Noise());
    }

    public void finishLaunching(String[] args)
    {
        try
        {
            Application app = Application.GetApplication();
            app.setTerminateWhenLastWindowClosed(true);
            app.setEventHandler(new Application.EventHandler()
            {
                @Override
                public void handleQuitAction(Application app, long time)
                {
                    plasma.stop();
                    app.terminate();
                }
            });
            
            plasma = new Noise.Plasma(IS3D);
            plasma.start();
        }
        catch (Throwable t)
        {
            t.printStackTrace();
        }
    }
    
    class NoisePixels
    {
        final boolean is3d;
        Timer timer = null;
        Window window = null;
        View view = null;
        ByteBuffer data = null;
        Pixels pixels = null;
        Random random = null;
        
        NoisePixels()
        {
            this(true);
        }
        
        NoisePixels(final boolean is3d)
        {
            this.is3d = is3d;
        }

        void start()
        {
            this.random = new Random();
            
            Application app = Application.GetApplication();
            this.window = app.createWindow(null, Screen.getMainScreen(), Window.TITLED|Window.CLOSABLE);
            this.window.setResizable(true);
            this.window.setEventHandler(new Window.EventHandler()
            {
                @Override
                public void handleWindowEvent(Window window, long time, int type)
                {
                    switch (type)
                    {
                        case WindowEvent.CLOSE:
                            close();
                            break;
                        case WindowEvent.DESTROY:
                            stop();
                            break;
                    }
                }
            });
            
            MenuBar menuBar = app.createMenuBar();
            app.installDefaultMenus(menuBar);
            this.window.setMenuBar(menuBar);
            
            Pen pen = null;
            if (this.is3d == true)
            {
                pen = new Noise.PlasmaGraphics3D();
            }
            else
            {
                pen = new Noise.PlasmaGraphics2D();
            }
            
            this.view = app.createView(pen);
            this.view.setEventHandler(new View.EventHandler()
            {
                @Override
                public void handleViewEvent(View view, long time, int type)
                {
                    switch (type)
                    {
                        case ViewEvent.REPAINT:
                            //System.err.println("JAVA: ViewEvent.REPAINT");
                            view.getPen().paint(System.nanoTime(), view.getWidth(), view.getHeight());
                            break;
                    }
                }
            });
            
            this.window.setView(this.view);
            
            if (this.is3d == true)
            {
                this.window.setTitle("Noise 3D");
            }
            else
            {
                this.window.setTitle("Noise 2D");
            }
            this.window.setPosition(125, 125);
            this.window.setSize(500, 250);
            if (this.window.isDecorated() == false)
            {
                this.window.setUndecoratedMoveRectangle(this.window.getHeight());
            }
            this.window.setBackground(1.0f, 0.0f, 0.0f); // RED
            this.window.setVisible(true);
            
            if (Noise.ANIMATE)
            {
                this.timer = Application.GetApplication().createTimer(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        // drawing from non UI thread, just like JFX
                        animate();
                    }
                });
                this.timer.start(1000/Noise.FPS);
            }
        }
        
        void update(int width, int height)
        {
            int size = width*height*4;
            this.data.rewind();
            {
                for (int i=0; i<size; i+=4)
                {
                    int pixel = this.random.nextInt();
                    this.data.putInt(pixel | 0xff000000);
                }
            }
            this.data.rewind();
            
            if ((this.pixels == null) || (Noise.REUSE_PIXELS == false) || (this.pixels.getWidth() != width) || (this.pixels.getHeight() != height))
            {
                this.pixels = Application.GetApplication().createPixels(width, height, this.data);
            }
            this.view.uploadPixels(this.pixels);
        }
        
        void animate()
        {
            // skip animation if repainting already (live resize optimization)
            if (painting == false)
            {
                if (Noise.USE_3RD_THREAD == true)
                {
                    this.view.lock();
                    //System.err.println("JAVA 3RD THREAD PAINT BEGIN");
                    animate(this.view.getWidth(), this.view.getHeight());
                    //System.err.println("JAVA 3RD THREAD PAINT END");
                    this.view.unlock(false);
                }
                else
                {
                    this.view.scheduleRepaint();
                }
            }
        }
        public void animate(int width, int height)
        {
            if (this.window != null)
            {
//                if (width != this.view.getWidth())
//                {
//                    System.err.println("width != this.view.getWidth()");
//                }
//                if (width != this.window.getWidth())
//                {
//                    System.err.println("width != this.window.getWidth()");
//                }
//                if (this.view.getWidth() != this.window.getWidth())
//                {
//                    System.err.println("this.view.getWidth() != this.window.getWidth()");
//                }
//                
                if (DELAY > 0)
                {
                    try
                    {
                        Thread.currentThread().sleep(DELAY);
                    }
                    catch (InterruptedException ex)
                    {
                        Logger.getLogger(Noise.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                
                int size = width*height*4;
                if (Noise.REUSE_PIXELS == true)
                {
                    if (size < 1024*1024*4)
                    {
                        size = 1024*1024*4;
                    }
                }
                
                if ((this.data == null) || (Noise.REUSE_PIXELS == false) || (size > this.data.capacity()))
                {
                    this.data = ByteBuffer.allocateDirect(size);
                    this.data.order(ByteOrder.nativeOrder());
                    this.data.rewind();
                }
                update(width, height);
            }
        }
        
        void close()
        {
            this.window.close();
            stop();
        }
        
        synchronized void stop()
        {
            if (this.timer != null)
            {
                this.timer.stop();
            }
            this.timer = null;
            this.window = null;
        }
    }
    
    boolean painting;
    class Plasma extends NoisePixels
    {
        public Plasma(final boolean is3d)
        {
            super(is3d);
        }
    }
    
    class PlasmaGraphics2D extends Graphics2D
    {
        @Override public void paint(long timeStamp, int width, int height)
        {
            painting = true;
            {
                set(Color.YELLOW);
                fillRect(0, 0, width, height);
                
                plasma.animate(width, height);
            }
            painting = false;
        }
    }
    class PlasmaGraphics3D extends Graphics3D
    {
        @Override public void paint(long timeStamp, int width, int height)
        {
            //System.err.println("JAVA BEGIN: paint");
            painting = true;
            {
                set(Color.YELLOW);
                fillRect(0, 0, width, height);
                
                plasma.animate(width, height);
            }
            painting = false;
            //System.err.println("JAVA END: paint");
        }
    }
}
