/*
 * JavaCL - Java API and utilities for OpenCL
 * http://javacl.googlecode.com/
 *
 * Copyright (c) 2009-2011, Olivier Chafik (http://ochafik.com/)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Olivier Chafik nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.nativelibs4java.opencl.demos.sobelfilter;

import org.bridj.JNI;
import javax.swing.*;

import com.nativelibs4java.opencl.*;
import com.nativelibs4java.opencl.util.*;
import com.nativelibs4java.opencl.demos.SetupUtils;
import com.nativelibs4java.util.Pair;
import java.awt.image.*;
import java.io.*;

import org.bridj.Platform;
import org.bridj.Pointer;
import static org.bridj.Pointer.*;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import java.awt.FileDialog;
public class SobelFilterDemo {

	static File chooseFile() {
		if (Platform.isMacOSX()) {
			FileDialog d = new FileDialog((java.awt.Frame)null);
			d.setMode(FileDialog.LOAD);
			d.show();
			String f = d.getFile();
			if (f != null)
				return new File(new File(d.getDirectory()), d.getFile());
		} else {
	        JFileChooser chooser = new JFileChooser();
	        if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION)
	        	return chooser.getSelectedFile();
		}
        return null;
	}
     public static void main(String[] args) {
        try {
            SetupUtils.failWithDownloadProposalsIfOpenCLNotAvailable();

            File imageFile = chooseFile();
            if (imageFile == null)
            	return;
            
            BufferedImage image = ImageIO.read(imageFile);
            int width = image.getWidth(), height = image.getHeight();
            //int step = 32;
           // image = image.getSubimage(0, 0, (width / step) * step, (height / step) * step);
            //image = image.getSubimage(0, 0, 512, 512);//(width / step) * step, (height / step) * step);
            

            JFrame f = new JFrame("JavaCL Sobel Filter Demo");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            SobelFilterDemo demo = new SobelFilterDemo();
			Pair<BufferedImage, BufferedImage> imgs = demo.computeSobel(image);
			f.getContentPane().add("Center",
				//new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
				//	new JScrollPane(new JLabel(new ImageIcon(image))),
                    new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                        new JScrollPane(new JLabel(new ImageIcon(imgs.getFirst()))),
                        new JScrollPane(new JLabel(new ImageIcon(imgs.getSecond())))
                    ) {/**
						 * 
						 */
						private static final long serialVersionUID = 8267014922143370639L;

					{
                        setResizeWeight(0.5);
                    }}
                //)
			);

            f.pack();
            f.setVisible(true);
        } catch (Throwable th) {
            th.printStackTrace();
            SetupUtils.exception(th);
        }
    }
     
    CLContext context;
    CLQueue queue;
    SimpleSobel sobel;
    ReductionUtils.Reductor<Float> floatMinReductor;
    

    public SobelFilterDemo() throws IOException, CLBuildException {
        context = JavaCL.createBestContext();
        queue = context.createDefaultQueue();
        sobel = new SimpleSobel(context);
        floatMinReductor = ReductionUtils.createReductor(context, ReductionUtils.Operation.Max, OpenCLType.Float, 1);

    }

    public static int roundUp(int group_size, int global_size)
    {
        int r = global_size % group_size;
        return r == 0 ? global_size : global_size + group_size - r;
    }
    public Pair<BufferedImage, BufferedImage> computeSobel(BufferedImage img) throws IOException, CLBuildException {
        int width = img.getWidth(), height = img.getHeight();

        int dataSize = height * width;
        Pointer<Integer> pixels = Pointer.pointerToInts(img.getRGB(0, 0, width, height, null, 0, width));
        CLImage2D inputImage = context.createImage2D(CLMem.Usage.Input, new CLImageFormat(CLImageFormat.ChannelOrder.ARGB, CLImageFormat.ChannelDataType.UnsignedInt8), width, height, width * 4, pixels.getBuffer(), true);
        //CLImage2D inputImage = context.createImage2D(CLMem.Usage.Input, img, false);

        CLBuffer<Float>
            gradients = context.createBuffer(CLMem.Usage.InputOutput, Float.class, dataSize),
            directions = context.createBuffer(CLMem.Usage.InputOutput, Float.class, dataSize);

        CLEvent evt = sobel.simpleSobel(queue,
            inputImage,
            width,
            height,
            gradients,
            directions,
            new int[] { width, height },
            null//new int[] { 1, 1 }
        );

        //queue.finish();
        
        //float[] test = new float[1000];
        //gradients.read(queue).get(test);

        float gradientMax = floatMinReductor.reduce(queue, gradients, dataSize, 32, evt).get();
        float dirMax = floatMinReductor.reduce(queue, directions, dataSize, 32, evt).get();

        //CLEvent.waitFor(evtGradMax, evtDirMax);

        CLBuffer<Integer> gradientPixels = context.createBuffer(CLMem.Usage.Output, Integer.class, dataSize);
        CLBuffer<Integer> directionPixels = context.createBuffer(CLMem.Usage.Output, Integer.class, dataSize);

        //CLEvent evtGrad =
        sobel.normalizeImage(queue, gradients, gradientMax, (CLBuffer)gradientPixels, new int[] { dataSize }, null);
        //CLEvent evtDir =
        sobel.normalizeImage(queue, directions, dirMax, (CLBuffer)directionPixels, new int[] { dataSize }, null);

        queue.finish();
        
        BufferedImage gradientsImage = getRowsOrderImage(queue, gradientPixels, width, height);//, evtGrad);
        BufferedImage directionsImage = getRowsOrderImage(queue, directionPixels, width, height);//, evtDir);

        return new Pair<BufferedImage, BufferedImage>(gradientsImage, directionsImage);
    }
    static BufferedImage getRowsOrderImage(CLQueue queue, CLBuffer<Integer> buffer, int width, int height, CLEvent... eventsToWaitFor) {
        queue.finish();
        BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        int[] pixels = buffer.read(queue, eventsToWaitFor).getInts(width * height);
        img.setRGB(0, 0, width,height, pixels, 0, width);
        return img;
    }
}

