package mosaik;

import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;

import util.Tools;
import util.simpleIO.Out;

import dataBase.DataBase;
import dataBase.TimedAction;
import dataBase.subPicture.SubPicture;

public class MosaikFunctions {
	
	public static void main(String[] args) throws IOException, ClassNotFoundException {
		
		int partsX = 50;
		int partsY = 50;
		float resize = 1f;
		File searchPicFile = new File("./testdaten/big/_8147226.JPG");
		//File searchPicFile = new File("./testdaten/big/_8147228.JPG");
		File dbFile        = new File("./testdaten/alt/DataBase0.db");
		
		BufferedImage mosaikImage = makeMosaik(searchPicFile, dbFile, partsX, partsY, resize);
		ImageIO.write(mosaikImage, "png", new File("mosaikImage.png"));
		//ImageIO.write(mosaikImage, "jpg", new File("mosaikImage.jpg"));
		//storeJPG(mosaikImage, new File("mosaikImage_4+.jpg"), 1);
		//ImageIO.write(mosaikImage, "tif", new File("mosaikImage.tif"));
		Out.pl("Fertig....File gespeichert"); 
	}
	
	/**
	 * 
	 * @param image
	 * @param file
	 * @param quality - 0...1
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	public static void storeJPG(BufferedImage image1, File file, float quality) throws FileNotFoundException, IOException{
		
		// You first need to enumerate the image writers that are available to your configuration:
		@SuppressWarnings("rawtypes")
		Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
		
		// Then, choose the first image writer available 
		// (unless you want to choose a specific writer) and create an ImageWriter instance:
		ImageWriter writer = (ImageWriter)iter.next(); // instantiate an ImageWriteParam object with default compression options
		
		ImageWriteParam iwp = writer.getDefaultWriteParam();
		
		// Now, we can set the compression quality:
		iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
		iwp.setCompressionQuality(1);   // an integer between 0 and 1, 1 specifies minimum compression and maximum quality
		
		//Output the file:
		FileImageOutputStream output = new FileImageOutputStream(file);
		writer.setOutput(output);
		IIOImage image = new IIOImage(image1, null, null);
		writer.write(null, image, iwp);
		writer.dispose();
	}
	
	public static void makeDatabase(File dir, File dbFile) throws IOException{
		
		DataBase db = new DataBase(dir);
		ObjectOutputStream obj_out = new ObjectOutputStream( new FileOutputStream(dbFile) );
		obj_out.writeObject(db);
		obj_out.close();
	}
	
	public static BufferedImage makeMosaik(final File searchPicFile, File dbFile, final int partsX, int partsY, float resize) throws FileNotFoundException, IOException, ClassNotFoundException{
		
		if(dbFile==null)
			dbFile = new File("defaultDB.db");
		
		Out.pl("> Load searchPicFull:");
		final BufferedImage searchPicFull = ImageIO.read(searchPicFile);
		int w = (searchPicFull.getWidth()), 				h = (searchPicFull.getHeight());
		int wFinal = (int) (searchPicFull.getWidth()*resize), 	hFinal = (int) (searchPicFull.getHeight()*resize);
		final float sliceWidth = (w/(float)partsX), 				sliceHeight = (h/(float)partsY);
		final float sliceWidthFinal = (wFinal/(float)partsX), 	sliceHeightFinal = (hFinal/(float)partsY);
		
		float tmpX = sliceWidthFinal*partsX;
		float tmpY = sliceHeightFinal*partsY;
		
		Out.pl("> Load DB:");
		ObjectInputStream obj_in = new ObjectInputStream( new FileInputStream(dbFile) );
		Object obj = obj_in.readObject();  
		final DataBase db = (DataBase) obj;
		Out.pl("  "+db.toString());
		
		Out.pl("> Search:");
		int processors = Tools.getAvailableProcessors();
		//File imageFile = new File("testdatenfinalImg.jpg");
		//File imageFile = new File(folder+"1.jpg");
		//BufferedImage searchPic = ImageIO.read(searchPicFile);
		
		BufferedImage finalImg = new BufferedImage(wFinal, hFinal, BufferedImage.TYPE_INT_RGB); 
		final Graphics2D finalGraphic = finalImg.createGraphics();
		finalGraphic.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 
		Thread[] threads = new Thread[processors];
		int threadIndex=0;
		//int num = 0;  
		for (int y = 0; y<partsY; y++) {  
			Out.pl("> Search up pic["+y+"][x]... ");
			
			//final int xFinal = x;
			final int yFinal = y;
			
			waitForThread(threads, threadIndex);
			final int threadID = threadIndex;
			
			threads[threadIndex] = new TimedAction(0) {
				
				@Override
				public void action() throws Exception {

					for (int x = 0; x<partsX; x++) {  

						int offsetX = (int) (sliceWidth*x);
						int offsetY = (int) (sliceHeight*yFinal);
						int tmpW = (int)(sliceWidth*(x+1)) - offsetX;
						int tmpH = (int)(sliceHeight*(yFinal+1) - offsetY);
						
						int offsetXFinal = (int) (sliceWidthFinal*x);
						int offsetYFinal = (int) (sliceHeightFinal*yFinal);
						int tmpWF = (int)(sliceWidthFinal*(x+1)) - offsetXFinal;
						int tmpHF = (int)(sliceHeightFinal*(yFinal+1)) - offsetYFinal;

						SubPicture searchPic = new SubPicture(searchPicFile, searchPicFull, offsetX, offsetY, tmpW, tmpH);
						SubPicture pic = db.search(searchPic, threadID).get(0);		// get best result

						BufferedImage picFull = ImageIO.read(pic.imageFile);
						//Out.pl("> Searched up pic["+y+"]["+0+"] '"+pic.imageFile.getName()+"'");

						finalGraphic.drawImage(picFull, offsetXFinal, offsetYFinal, tmpWF, tmpHF, null);
					}//for j

				}//action()

			};//thread
			threadIndex = (threadIndex+1) % threads.length;
		}//for y
		
		for (int i=0; i<threads.length; i++) { 
			waitForThread(threads, i);
		}
		
		Out.pl("> Finished search.");
		return finalImg;
	}
	
	private static void waitForThread(Thread[] threads, int i){
		
		if(threads[i]!=null && threads[i].isAlive()){
			try {
				threads[i].join();
			} catch (InterruptedException e) {
				// ignore
			}
		}
	}
	
}
