/**
 * Copyright 2010 Neuroph Project http://neuroph.sourceforge.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.neuroph.contrib.imgrec;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;

/**
 * This class uses a given Robot object to sample images from the screen at an 
 * arbitrary sampling resolution.  The coordinates of the scanning rectangle 
 * is (x, y) expressed as a fraction of the screen resolution, with the width also being
 * a fraction.
 * 
 * For example, when the screen is 800x600 resolution, to scan a rectangle with
 * xy coordinates (400,300) to xy (600, 400), use the following rectangle as an argument:
 * 
 * new Rectangle2D.Double(0.5, 0.5, 0.25, 0.16667)
 * 
 * Scanning using sampling is faster than scanning using a screenshot, but is
 * prone to introduce tearing and shearing into the scanned image.  Scanning
 * from a screenshot has no tearing or shearing at the cost of speed.
 *
 * Also provides method for downsampling (scaling) image.
 * This class is based on the code from tileclassification by Jon Tait.
 * 
 * @author Jon Tait
 *
 */
public class ImageSampler
{
	/**
	 * Scans screen location using screenshot
	 * @param robot
	 * @param rectangleAsDecimalPercent
	 * @param samplingResolution
	 * @return image sample from the specified location
	 */
	public static BufferedImage scanLocationUsingScreenshot(Robot robot,
			Rectangle2D.Double rectangleAsDecimalPercent, Dimension samplingResolution)
	{
		return scanLocationUsingScreenshot(robot, rectangleAsDecimalPercent, samplingResolution, BufferedImage.TYPE_INT_RGB);
	}
	
	/**
	 * Scans screen location using screenshot
	 * @param robot
	 * @param rectangleAsDecimalPercent
	 * @param samplingResolution
	 * @param imageType
	 * @return image sample from the specified location
	 */
	public static BufferedImage scanLocationUsingScreenshot(Robot robot,
			Rectangle2D.Double rectangleAsDecimalPercent, Dimension samplingResolution,
			int imageType)
	{
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		
		int inspectX = (int) Math.round((screenSize.width * rectangleAsDecimalPercent.x));
		int inspectY = (int) Math.round((screenSize.height * rectangleAsDecimalPercent.y));
		int inspectWidth = (int) Math.round((screenSize.width * rectangleAsDecimalPercent.width));
		int inspectHeight = (int) Math.round((screenSize.height * rectangleAsDecimalPercent.height));
		
		Rectangle screenRect = new Rectangle(inspectX, inspectY, inspectWidth, inspectHeight);
		BufferedImage screenCapture = robot.createScreenCapture(screenRect);
		
		
		return downSampleImage(samplingResolution, screenCapture, imageType);
	}

        /**
         * Scales image to the specified dimension
         * @param samplingResolution sampling resolution/image size
         * @param bigImg image to scale
         * @return image scaled/downsampled to the specified dimension/resolution
         */
	public static BufferedImage downSampleImage(
			Dimension samplingResolution, BufferedImage bigImg) {
		return downSampleImage(samplingResolution, bigImg, BufferedImage.TYPE_INT_RGB);
	}


        /**
         * Scales image to the specified dimension
         * @param samplingResolution sampling resolution/image size
         * @param bigImg image to scale
         * @param returnImageType type of the return image
         * @return image scaled/downsampled to the specified dimension/resolution
         */
	public static BufferedImage downSampleImage(Dimension samplingResolution,
			BufferedImage bigImg, int returnImageType) {
		int inspectX;
		int inspectY;
		int numberOfSamplesAcross = samplingResolution.width;
		int numberOfSamplesDown = samplingResolution.height;
		
		if(bigImg.getWidth() <= numberOfSamplesAcross || bigImg.getHeight() <= numberOfSamplesDown) {
			return bigImg;
		}
		
		BufferedImage img = new BufferedImage(
				numberOfSamplesAcross, numberOfSamplesDown, 
				returnImageType);
		
		double samplingIncrementX = bigImg.getWidth() / (samplingResolution.getWidth() - 1);
		double samplingIncrementY = bigImg.getHeight() / (samplingResolution.getHeight() - 1);
		
		double sampleX = 0;
		double sampleY = 0;
		for(int y=0; y < numberOfSamplesDown; y++) {
			for(int x=0; x < numberOfSamplesAcross; x++) {
				inspectX = (int) Math.round(sampleX);
				inspectY = (int) Math.round(sampleY);
				
				if(inspectX >= bigImg.getWidth()) {
					inspectX = bigImg.getWidth() - 1;
				}
				if(inspectY >= bigImg.getHeight()) {
					inspectY = bigImg.getHeight() - 1;
				}
				int color = bigImg.getRGB(inspectX, inspectY);
				img.setRGB(x, y, color);
				sampleX+=samplingIncrementX;
			}
			sampleX=0;
			sampleY+=samplingIncrementY;
		}
		
		return img;
	}
	
	/**
	 * Scans screen location using sampling
	 * @param robot an instance of java.awt.Robot for the scaned screen
	 * @param rectangleAsDecimalPercent
	 * @param samplingResolution
	 * @return image sample from the specified location
	 */
	public static BufferedImage scanLocationUsingSampling(Robot robot,
			Rectangle2D.Double rectangleAsDecimalPercent, Dimension samplingResolution)
	{
		return scanLocationUsingSampling(robot, rectangleAsDecimalPercent, samplingResolution, BufferedImage.TYPE_INT_RGB);
	}
	
	/**
	 * Scans screen location using sampling
	 * @param robot an instance of java.awt.Robot for the scaned screen
	 * @param rectangleAsDecimalPercent
	 * @param samplingResolution
	 * @param imageType
	 * @return image sample from the specified location
	 */
	public static BufferedImage scanLocationUsingSampling(Robot robot,
			Rectangle2D.Double rectangleAsDecimalPercent, Dimension samplingResolution,
			int imageType)
	{
		double slotULXAsDecimalPercent = rectangleAsDecimalPercent.x; 
		double slotULYAsDecimalPercent = rectangleAsDecimalPercent.y;
		double sampleIncrementAcrossAsDecimalPercent = rectangleAsDecimalPercent.width / (samplingResolution.getWidth()-1);
		double sampleIncrementDownAsDecimalPercent = rectangleAsDecimalPercent.height / (samplingResolution.getHeight()-1);
		
		int numberOfSamplesAcross = samplingResolution.width;
		int numberOfSamplesDown = samplingResolution.height;
		BufferedImage img = new BufferedImage(
				numberOfSamplesAcross, numberOfSamplesDown, 
				imageType);
		
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		
		waitForVerticalScreenRefresh();
		
		double inspectXAsDecimalPercent = slotULXAsDecimalPercent;
		double inspectYAsDecimalPercent = slotULYAsDecimalPercent;
		for(int y=0; y < numberOfSamplesDown; y++) {
			for(int x=0; x < numberOfSamplesAcross; x++) {
				int inspectX = (int) Math.round((screenSize.width * inspectXAsDecimalPercent));
				int inspectY = (int) Math.round((screenSize.height * inspectYAsDecimalPercent));
				Color color = robot.getPixelColor(inspectX, inspectY);
				img.setRGB(x, y, color.getRGB());
				inspectXAsDecimalPercent+=sampleIncrementAcrossAsDecimalPercent;
			}
			inspectXAsDecimalPercent = slotULXAsDecimalPercent;
			inspectYAsDecimalPercent+=sampleIncrementDownAsDecimalPercent;
		}
		return img;
	}

	/**
	 * Another name for this is "Vertical Sync".  Minimizes frame shearing.
	 */
	private static void waitForVerticalScreenRefresh()
	{
		Toolkit.getDefaultToolkit().sync();
	}
}
