package cz.vutbr.feec.utko.opencv.operators.RandomNoise;

import java.awt.Point;
import java.nio.ByteBuffer;
import java.util.Random;
import java.util.Vector;

import com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import com.googlecode.javacv.cpp.opencv_core.CvCloneFunc;
import com.googlecode.javacv.cpp.opencv_core.CvSize;
import com.googlecode.javacv.cpp.opencv_core.Cv_iplCloneImage;
import com.googlecode.javacv.cpp.opencv_core.IplImage;

/**
 * The implementation of the random generator noise to the image
 * 
 * @author Martin Hasmanda, Departement of the telecomunication, VUT Brno
  */
public class PseudoRandomNoise {

	/**
	 * Seed fro the pseudo random generator
	 */
	private long m_seed;

	/**
	 * The pseudo random generator from java.util.Random
	 */
	Random m_generator;
	
	/**
	 * Create an object with a specific seed for the random generator 
	 * @param seed Initial value for the random generator
	 */
	public PseudoRandomNoise(long seed)
	{
		m_seed = seed;
		m_generator = new Random(m_seed);
	}

	/**
	 * The metod for to test a regular image coordinates
	 * @param imageSize	Actual image size
	 * @param row rows index
	 * @param col cols index
	 * @return Returns 'True' if the row and col position is the regular position of the image
	 */
	private boolean CheckImageSize(CvSize imageSize, int row, int col)
	{
		return !( 
				row >= imageSize.height() || col >= imageSize.width() 
				|| col<0 || row<0 );
	}
	
	/**
	 * Generation of the random image points with the burst size points
	 * @param pointsCount	The number of the burst posititons
	 * @param pointsInSeed	The number points in the burst
	 * @param imageSize		The actual image size
	 * @param burstSize		The size of the burst points
	 * @return				List of the regular random noise points
	 */
	protected Vector<Point> GenerateCumulativePoints(
		    int pointsCount, 
		    int pointsInSeed, 
		    CvSize imageSize,
		    int burstSize)
	{
		Vector<Point> results = new Vector<Point>();

		for (int i = 0; i < pointsCount; i++) {
			int valueX = Math.abs(m_generator.nextInt())%imageSize.width();
			int valueY = Math.abs(m_generator.nextInt())%imageSize.height();
			
			System.out.println(valueX + ", " + valueY);
			
			
			for (int j = 0; j < pointsInSeed; j++) {
				int burstX = valueX + (int)(m_generator.nextGaussian()*burstSize/2);
				int burstY = valueY + (int)(m_generator.nextGaussian()*burstSize/2);
				
				if(CheckImageSize(imageSize, burstY, burstX))
					results.add(new Point(burstX, burstY));
			}
		}
		
		return results;
	}
	
	/**
	 * Generation of the random image points with the burst size points
	 * @param inputPoints	List of the burst positions
	 * @param pointsInSeed	The number points in the burst
	 * @param imageSize		The actual image size
	 * @param burstSize		The size of the burst points
	 * @return				List of the regular random noise points
	 */
	protected Vector<Point> GenerateCumulativePoints(
			Vector<Point> inputPoints, 
		    int pointsInSeed, 
		    CvSize imageSize,
		    int burstSize)
	{
		Vector<Point> results = new Vector<Point>();

		for (int i = 0; i < inputPoints.size(); i++) {
			
			for (int j = 0; j < pointsInSeed; j++) {
				int burstX = inputPoints.get(i).x + (int)(m_generator.nextGaussian()*burstSize/2);
				int burstY = inputPoints.get(i).y + (int)(m_generator.nextGaussian()*burstSize/2);
				
				if(CheckImageSize(imageSize, burstY, burstX))
					results.add(new Point(burstX, burstY));
			}
		}
		
		return results;
	}
	
	/**
	 * Generate a random points in the image
	 * @param pointsCount	The number of points
	 * @param imageSize		The actual image size
	 * @return				List of the regular random noise points
	 */
	protected Vector<Point> GeneratePoints(
		    int pointsCount, 
		    CvSize imageSize
		    )
	{
		Vector<Point> results = new Vector<Point>();

		for (int i = 0; i < pointsCount; i++) {
			int valueX = Math.abs(m_generator.nextInt())%imageSize.width();
			int valueY = Math.abs(m_generator.nextInt())%imageSize.height();
						
			if(CheckImageSize(imageSize, valueY, valueX))
				results.add(new Point(valueX, valueY));
		}
		
		return results;
	}

	/**
	 * Generate a random points in the image
	 * @param inputPoints	The vector of the input points
	 * @param imageSize		The actual image size
	 * @return				List of the regular random noise points
	 */
	protected Vector<Point> GeneratePoints( Vector<Point> inputPoints, CvSize imageSize )
	{
		Vector<Point> results = new Vector<Point>();

		for (int i = 0; i < inputPoints.size(); i++) {
			if(CheckImageSize(imageSize, inputPoints.get(i).y, inputPoints.get(i).x))
				results.add(new Point(inputPoints.get(i).x, inputPoints.get(i).y));
		}
		
		return results;
	}
	
	/**
	 * Draw white points to the image 
	 * @param inputImage	Input image
	 * @param inputPoints	Intput points in the regular positions
	 * @return				Image copy from the inputImage with white points from inputPoints
	 */
	private IplImage DrawPoints(IplImage inputImage, Vector<Point> inputPoints)
	{
		//IplImage result = IplImage.createCompatible(inputImage);
		IplImage result = cvCloneImage(inputImage);
	
		ByteBuffer data = result.getByteBuffer();
		
		for (int i = 0; i < inputPoints.size(); i++) {
			int iRow = inputPoints.get(i).y;
			int iCol = inputPoints.get(i).x; 
			data.put(iRow*inputImage.widthStep()+iCol, (byte)255);
		}
		
		return result;
	}
	
	/**
	 * Generate and draw random points to the image
	 * @param inputImage		Input image
	 * @param noisePointCount	The number of the random generated points
	 * @return					Output image with white points
	 */
	public IplImage DrawRandomPoints(IplImage inputImage, int noisePointCount)
	{
		Vector<Point> points = this.GeneratePoints(noisePointCount, inputImage.cvSize());
		return this.DrawPoints(inputImage, points);
	}
	
	/**
	 * Generate and draw random points to the image
	 * @param inputImage		Input image
	 * @param noisePointCount	The number of the random generated points
	 * @param noiseBurstCount	The number of the normal random generated points in the points positions
	 * @param noiseSize			The size of the burst area
	 * @return					Output image with white points
	 */
	public IplImage DrawRandomPoints(
			IplImage inputImage, int noisePointCount, 
			int noiseBurstCount, int noiseSize )
	{
		Vector<Point> points = this.GenerateCumulativePoints(
				noisePointCount, noiseBurstCount, inputImage.cvSize(), noiseSize);
		return this.DrawPoints(inputImage, points);
	}
	
	/**
	 * Generate and draw points to the image
	 * @param inputImage	Input image
	 * @param position		The position of the noise
	 * @return				Output image with white points
	 */
	public IplImage DrawRandomPoints( IplImage inputImage, Point position )
	{
		Vector<Point> pointsInput = new Vector<Point>();
		pointsInput.add(position);
		
		Vector<Point> points = this.GeneratePoints(pointsInput, inputImage.cvSize());
		return this.DrawPoints(inputImage, points);
	}
	
	/**
	 * Generate and draw points to the image
	 * @param inputImage	Input image
	 * @param positions		The noise positions
	 * @return				Output image with white points
	 */
	public IplImage DrawRandomPoints( IplImage inputImage, Vector<Point> positions )
	{
		Vector<Point> points = this.GeneratePoints(positions, inputImage.cvSize());
		return this.DrawPoints(inputImage, points);
	}
	
	/**
	 * Generate and draw points to the image
	 * @param inputImage		Input image
	 * @param position			The position of the noise
	 * @param noiseBurstCount	The number of the normal random generated points in the points positions
	 * @param noiseSize			The size of the burst area
	 * @return					Output image with white points
	 */
	public IplImage DrawRandomPoints(
			IplImage inputImage, Point position, 
			int noiseBurstCount, int noiseSize )
	{
		Vector<Point> pointsInput = new Vector<Point>();
		pointsInput.add(position);
		
		Vector<Point> points = this.GenerateCumulativePoints(
				pointsInput, noiseBurstCount, inputImage.cvSize(), noiseSize);
		return this.DrawPoints(inputImage, points);
	}
	
	/**
	 * Generate and draw points to the image
	 * @param inputImage		Input image
	 * @param positions			The list of the noise positions 
	 * @param noiseBurstCount	The number of the normal random generated points in the points positions
	 * @param noiseSize			The size of the burst area
	 * @return					Output image with white points
	 */
	public IplImage DrawRandomPoints(
			IplImage inputImage, Vector<Point> positions, 
			int noiseBurstCount, int noiseSize )
	{
		Vector<Point> points = this.GenerateCumulativePoints(
				positions, noiseBurstCount, inputImage.cvSize(), noiseSize);
		return this.DrawPoints(inputImage, points);
	}
}
