package mcmas.core.examples;
import java.util.Arrays;

import mcmas.core.MCM;
import mcmas.core.MCMCommandQueue;
import mcmas.core.MCMContext;
import mcmas.core.MCMKernel;
import mcmas.core.MCMMem;
import mcmas.core.MCMProgram;

import org.jocl.Pointer;



public class MCMCellular {
	
	private static final String source =
			"kernel void cellular1(constant float* input, global float* output) {  \n" +
			"    int cellHeight = get_local_size(0);                                 \n" +
			"    int cellWidth  = get_local_size(1);                                 \n" +
			"    int cellSize   = cellHeight * cellWidth;                            \n" +
			"    int i          = get_global_id(0);                                  \n" +
			"    int j          = get_local_id(0);                                   \n" +
			"    int k          = get_local_id(1);                                   \n" +
			"    int index      = cellSize * i + cellWidth * j + k;                  \n" +
			"    if (input[index] > 0.5f) {                                           \n" +
			"        output[index] = input[index] / 2;                                \n" +
			"    } else {                                                            \n" +
			"        output[index] = input[index] * 2;                                \n" +
			"    }                                                                   \n" +
			"}                                                                       \n";
	
	private final int nbCells;
	private final int cellHeight;
	private final int cellWidth;
	private final float[][][] cells;
	
	public MCMCellular(int nbCells, int cellHeight, int cellWidth) {
		this.nbCells = nbCells;
		this.cellHeight = cellHeight;
		this.cellWidth = cellWidth;
		this.cells = new float[nbCells][][];
		
		for (int i = 0; i < nbCells; i++) {
			float[][] cell = new float[cellHeight][cellWidth];
			for (int j = 0; j < cellHeight; j++) {
				for (int k = 0; k < cellWidth; k++) {
					cell[j][k] = (float) Math.random();
				}
			}
			cells[i] = cell;
		}
	}
	
	@Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		
		for (float[][] cell : cells) {
			for (float[] line : cell) {
				buffer.append(Arrays.toString(line));
				buffer.append('\n');
			}
			buffer.append('\n');
		}
		
		return new String(buffer);
	}
	
	public void step() {
		MCMContext context = new MCMContext();
		float[] data = linearize();
		MCMMem input = context.newBuffer().Using(data).Constant().b();
		// context.createBuffer(data.length, OCL.FLOAT, OCL.MEM_RO, Pointer.to(data));
		MCMMem output = context.newBuffer().Size(data.length, MCM.FLOAT).b();
		//context.createBuffer(data.length, OCL.FLOAT, OCL.MEM_RW);
		
		MCMCommandQueue queue = context.createCommandQueue();
		MCMProgram program = context.createProgram(source);
		MCMKernel kernel = program.createKernel("cellular1");
		kernel.setArguments(input, output);
		
		queue.enqueueKernel(kernel, 2, new long[] {0, 0}, new long[] {nbCells, 1}, new long[] {cellHeight, cellWidth});
		//queue.enqueueCopyBuffer(output, input, 0, 0, input.getSize());
		
		queue.blockingReadBuffer(output, Pointer.to(data), 0, output.getSize());
		delinearize(data);
	}
	
	private float[] linearize() {
		float[] result = new float[nbCells * cellHeight * cellWidth];
		final int cellSize = cellHeight * cellWidth;
		
		for (int i = 0; i < nbCells; i++) {
			for (int j = 0; j < cellHeight; j++) {
				for (int k = 0; k < cellWidth; k++) {
					result[i * cellSize + j * cellWidth + k] = cells[i][j][k];
				}
			}
		}
		
		return result;
	}
	
	private void delinearize(float[] data) {
		final int cellSize = cellHeight * cellWidth;
		
		for (int i = 0; i < nbCells; i++) {
			for (int j = 0; j < cellHeight; j++) {
				for (int k = 0; k < cellWidth; k++) {
					cells[i][j][k] = data[i * cellSize + j * cellWidth + k];
				}
			}
		}
	}
	
	public static void main(String[] args) {
		MCMCellular cellular = new MCMCellular(10, 10, 10);
		System.out.println(cellular);
		cellular.step();
		System.out.println(cellular);
	}

}
