/**
 * 
         -------- Copyright (C) [2012]  [Kristofer SCHWEIGER & Niklas FLORIAN1]---------

    This program is free software; you can redistribute it and/or modify it under the terms of the
 *  GNU General Public License as published by the Free Software Foundation; either version 2 of the License,
 *  or (at your option) any later version.
    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *  See the GNU General Public License for more details.
 * 
    You should have received a copy of the GNU General Public License along with this program; if not, 
 *  write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA

 * 
 * For any licensing issues please contact us under following adress: Mosaik.Projekt.2012@gmail.com
 */


package dataBase.subPicture;

import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;

import mosaik.MosaikConstants;

import dataBase.search.distance.AbstractDistance;

import util.Tools;

public class SubPicture implements Serializable {
	
	private static final long serialVersionUID = 8L;
	/* predefined, important for data base and search */
	public static final int partsX=MosaikConstants.partsX, partsY=MosaikConstants.partsY;
	
	/** used temporary at search **/
	transient public float[] distance_ToSearchPic;
	transient public SubPicture[] distance_searchPic;
	
	//private BufferedImage image;
	//private DataBuffer dataBuffer;
	//private boolean hasAlpha;
	public File imageFile;
	public PixelByte[] pixels = new PixelByte[partsY*partsX];
	public PixelFloat avg;
	
	/**
	 * Constructor: Full, generates SubPicture information
	 * @param image - image with data
	 * @param offsetX - offset (start offset)
	 * @param offsetY - offset (start offset)
	 * @param lengthX - limit (width)
	 * @param lengthY - limit (height)
	 */
	public SubPicture(File imageFile,BufferedImage image, float offsetX,float offsetY, float lengthX,float lengthY){
		
		assert offsetX>0 && offsetY>0 && lengthX>0 && lengthY>0;
		assert (lengthX-offsetX)>partsX && (lengthY-offsetY)>partsY;
		
		this.imageFile = imageFile;
		initStuff();
		initPixels(offsetX,offsetY, lengthX,lengthY,  image);
		initAVG();
	}
	/**
	 * Constructor: simple
	 * @param image
	 */
	public SubPicture(File imageFile, BufferedImage image){
		this(imageFile,image, 0,0, image.getWidth(),image.getHeight());
		initStuff();
	}
	
	/**
	 * @param all - see above
	 */
	private void initPixels(float offX,float offY, float lenX,float lenY,
			BufferedImage image){
		
		//Out.pl("> Init subpicture for X="+offX+"-"+w+", Y="+offY+"-"+h);
		WritableRaster raster = image.getRaster();
		int[] pixel;
		if(image.getColorModel().hasAlpha())	// needed to avoid exceptions
			pixel = new int[4];					// -
		else									// -
			pixel = new int[3];					// -
		
		float partLengthX = lenX/(float)partsX;	// single part length for pixel calculation
		float partLengthY = lenY/(float)partsY;	// -
		
		if(partLengthX >= 1f && partLengthY >= 1f){	// the usual algorithm:
			
			offX += 0.5f;							// for better rounding
			offY += 0.5f;							// -
			
			for(int y=0; y<partsY; y++){
				for(int x=0; x<partsX; x++){
					
					int startX = (int) (offX + x*partLengthX);
					int stopX  = (int) (offX + (x+1)*partLengthX);
					
					int startY = (int) (offY + y*partLengthY);
					int stopY  = (int) (offY + (y+1)*partLengthY);
					
					pixels[y*partsX+x] = getSingleSubPicture(startX, startY, stopX, stopY,  pixel, raster);
				}
			}
			
		}
		else{										// algorithm for small pictures:
			
			lenX -= 1;
			lenY -= 1;
			partLengthX = lenX/(float)partsX;	// single part length for pixel calculation
			partLengthY = lenY/(float)partsY;	// -
			
			for(int y=0; y<partsY; y++){
				for(int x=0; x<partsX; x++){
					
					int startX = (int) (offX + x*partLengthX);
					int stopX  = (int) (offX + (x+1)*partLengthX);
					if(startX==stopX)
						stopX++;
					
					int startY = (int) (offY + y*partLengthY);
					int stopY  = (int) (offY + (y+1)*partLengthY);
					if(startY==stopY)
						stopY++;
					
					pixels[y*partsX+x] = getSingleSubPicture(startX, startY, stopX, stopY,  pixel, raster);
				}
			}
		}
		

	}
	
	/**
	 * Init average value.
	 */
	private void initAVG(){
		
		int pixelCount = 0;				// Pixel counter for average
		float avgR     = 0;				// Average RGB
		float avgG     = 0;				// -
		float avgB     = 0;				// -
		
		for(int y=0; y<partsY; y++){
			for(int x=0; x<partsX; x++){
				
				PixelByte p = pixels[y*partsX+x];
				
				avgR += (p.r1&0xff);			// sum up pixel data
				avgG += (p.g1&0xff);			// -
				avgB += (p.b1&0xff);			// -
				pixelCount++;							// count number of pixel
			}
		}
		
		avgR /= pixelCount;
		avgG /= pixelCount;
		avgB /= pixelCount;
		PixelFloat avg = new PixelFloat(avgR, avgG, avgB);

		assert avg.test();
		this.avg = avg;
	}
	
	/**
	 * Calculate Distance internally
	 * @param distanceFkt
	 * @param searchPic
	 */
	public void setDistanceToSearchPic(AbstractDistance distanceFkt, SubPicture searchPic, int threadID){
		
		SubPicture lastSearchPic = distance_searchPic[threadID];
			
//			distance_ToSearchPic = new float[processors];
//		distance_searchPic = new SubPicture[processors];
		
		if(lastSearchPic != searchPic){
			float tmp = distanceFkt.getDistance(this, searchPic);
			//Out.pl(" >>> Setting distance at '"+imageFile.getName()+"' from "+distance_ToSearchPic+" to "+tmp);
			distance_ToSearchPic[threadID] = tmp;
		}
	}
	
	/**
	 * 
	 * @param startX - offset (start offset)
	 * @param startY - offset (start offset)
	 * @param stopX - limit (width)
	 * @param stopY - limit (height)
	 * @param pixel - tmp array for data, length is 3 or 4
	 * @param raster - raster with pixel data
	 * @return Pixel object for this picture section.
	 */
	private PixelByte getSingleSubPicture(int startX, int startY, int stopX, int stopY,
			int[] pixel, WritableRaster raster){
		
		//Out.pl(" >> Making subpicture for X="+startX+"-"+stopX+", Y="+startY+"-"+stopY);
		
		int pixelCount = 0;				// Pixel counter for average
		float avgR     = 0;				// Average RGB
		float avgG     = 0;				// -
		float avgB     = 0;				// -
		
		for(int y=startY; y<stopY; y++){
			for(int x=startX; x<stopX; x++){
				
				raster.getPixel(x, y, pixel);			// load picture pixel data

				avgR += pixel[0];						// sum up pixel data
				avgG += pixel[1];						// -
				avgB += pixel[2];						// -
				pixelCount++;							// count number of pixel
			}
		}
		
		pixel[0] = (int) (avgR/pixelCount+0.5f);		// get absolute value, rounded
		pixel[1] = (int) (avgG/pixelCount+0.5f);		// -
		pixel[2] = (int) (avgB/pixelCount+0.5f);		// -
		PixelByte tmp = new PixelByte(pixel);			// return new pixel information

		return tmp;
	}
	
	@Override
	public String toString(){
		
		StringBuffer s = new StringBuffer(120);
		s.append("'"+imageFile.getName()+"' avg="+avg+"");
		
//		for(int y=0; y<partsY; y++){
//			for(int x=0; x<partsX; x++){
//				s.append( pixels[y][x].toString() );
//				if(x+1<partsX)
//					s.append( ", " );
//			}
//			if(y+1<partsY)
//				s.append( "; " );
//		}
		
		return s.toString();
	}
	
	/**
	 * Manual writing. (less size)
	 * @param out
	 * @throws IOException
	 */
	private void writeObject(java.io.ObjectOutputStream out) throws IOException {
		
		out.writeObject(imageFile);
		
		int size = partsY*partsX;
		for(int i=0; i<size; i++){
			pixels[i].writeObjectNew(out);
		}
		avg.writeObjectNew(out);
	}
	/**
	 * Manual reading. (less size)
	 * @param in
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
		imageFile = (File) in.readObject();
		
		pixels = new PixelByte[partsY*partsX];
		int size = partsY*partsX;
		for(int i=0; i<size; i++){
			pixels[i] = new PixelByte().readObjectNew(in);
		}
		
		avg = new PixelFloat().readObjectNew(in);
		initStuff();
	}
	
	private void initStuff(){
		int processors = Tools.getAvailableProcessors();
		distance_ToSearchPic = new float[processors];
		distance_searchPic = new SubPicture[processors];
	}
}
