/**
 * 
         -------- 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 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.PictureBuffer;
import dataBase.TimedAction;
import dataBase.subPicture.SubPicture;
import gui.MosaikApp;

public class MosaikFunctions {
	
//	public static void main(String[] args) throws IOException, ClassNotFoundException {
//		
//		int partsX = 100;
//		int partsY = 100;
//		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);
//		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 showConfiguration:
		@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 DataBase makeDatabase(File dir) throws IOException{
		
		DataBase db = new DataBase(dir);
		//ObjectOutputStream obj_out = new ObjectOutputStream( new FileOutputStream(dbFile) );
		//obj_out.writeObject(db);
		//obj_out.close();
		
		return db;
	}
	
	public static void storeDatabase(DataBase db, File dbFile) throws IOException{
		
		ObjectOutputStream obj_out = new ObjectOutputStream( new FileOutputStream(dbFile) );
		obj_out.writeObject(db);
		obj_out.close();
	}
	
	public static DataBase loadDatabase(File dbFile) throws IOException, ClassNotFoundException{
		
		ObjectInputStream obj_in = new ObjectInputStream( new FileInputStream(dbFile) );
		Object obj = obj_in.readObject();  
		DataBase db = (DataBase) obj;
		
		return db;
	}
	
	public static void addDatabase(DataBase db, DataBase db2) throws IOException, ClassNotFoundException{
		db.add(db2);
	}
	
	
	public static BufferedImage makeMosaik(final File searchPicFile, File dbFile, final int partsX, int partsY, float resize) throws FileNotFoundException, IOException, ClassNotFoundException{
		
		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());
		
		return makeMosaik(searchPicFile, db, partsX, partsY, resize);
	}
	
	public static BufferedImage makeMosaik(final File searchPicFile, final DataBase db, 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);
		
//		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:");
		//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();
		final PictureBuffer picBuffer = new PictureBuffer();
		finalGraphic.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); 
		
		int processors = Tools.getAvailableProcessors();
//		Thread[] threads = new Thread[1];		// testing
		Thread[] threads = new Thread[processors];
		int threadIndex=0;
		//int num = 0;  
		for (int y = 0; y<partsY; y++) {  
			
			//final int xFinal = x;
			final int yFinal = y;
			
			waitForThread(threads, threadIndex);
			
			Out.pl("> Search up pic["+y+"][x]... ");
                        MosaikApp.setProgressvalue();
                       
			final int threadID = threadIndex;
			threads[threadIndex] = new TimedAction(0) {
				
				@Override
				public void action() throws Exception {

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

//						MosaikConstants.subPictureGeneration.start();
						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;
						
//						float offsetX = (sliceWidth*x);
//						float offsetY = (sliceHeight*yFinal);
//						float tmpW = (sliceWidth*(x+1)) - offsetX;
//						float tmpH = (sliceHeight*(yFinal+1)) - offsetY;
//						
//						float offsetXFinal = (sliceWidthFinal*x);
//						float offsetYFinal = (sliceHeightFinal*yFinal);
//						float tmpWF = (sliceWidthFinal*(x+1)) - offsetXFinal;
//						float tmpHF = (sliceHeightFinal*(yFinal+1)) - offsetYFinal;

						SubPicture searchPic = new SubPicture(searchPicFile, searchPicFull, offsetX, offsetY, tmpW, tmpH);
//						MosaikConstants.subPictureGeneration.stop();
						
//						MosaikConstants.pictureSearch.start();
						SubPicture pic = db.search(searchPic, threadID).get(0);		// get best result
//						MosaikConstants.pictureSearch.stop();
						
//						MosaikConstants.pictureLoad.start();
						BufferedImage picFull = picBuffer.getPicture(pic.imageFile); //ImageIO.read(pic.imageFile);
//						MosaikConstants.pictureLoad.stop();
						
						//BufferedImage picFull = ImageIO.read(pic.imageFile);
						//Out.pl("> Searched up pic["+y+"]["+0+"] '"+pic.imageFile.getName()+"'");
						
//						MosaikConstants.pictureDraw.start();
//						finalGraphic.drawImage(picFull, (int)(offsetXFinal), (int)(offsetYFinal), (int)(tmpWF+0.99), (int)(tmpHF+0.99), null);
						finalGraphic.drawImage(picFull, offsetXFinal, offsetYFinal, tmpWF, tmpHF, null);
//						MosaikConstants.pictureDraw.stop();
						
					}//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
			}
		}
	}
	
}
