import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;

//import org.apache.log4j.Logger;

/**
 * Execute the stamping
 * @author JPR
 *
 */
public class StampleProcess
{
	private boolean stop; //if stop is true,  the processJob stop
	
	private MacroMethods macroMethods;
	static Logger logger = Logger.getLogger(StampleProcess.class.getName());

	/**
	 * Constructor
	 */
	public StampleProcess() 
	{
		stop = false;
	}

	/**
	 * process a stamping
	 * @param job to process
	 */
	public void processJob(Job job)
	{
		logger.info("stamping start : " + job.getjobFile().getName());

		logger.info("loading file");
		int pageErrorCount = 0;
		int pageProcessCount = 0;
		int firstPageNumber = job.getStartPage();
		int lastPageNumber;
		int stampleLastPageNumber;
		int hstep = 5;
		int vstep = 5;
		int marge = 15;
		ArrayList<Stamp> stampList = new ArrayList<Stamp>();
		BufferedImage white = null, screen;
		boolean stamplePlaceFound;
		int stamplePageInClipboard=0;
		int currentPage=0;
		boolean pdfIsOpen=false;
		boolean pdfStampleIsOpen=false;
		boolean retryMode=false;
		int nbTryOnPage=0;

		job.getResult().setResultFile(job.getResultFile());
		job.getResult().setStartJobDateTime(new Date());
		
		stop = ServiceController.isStop() || Run.stopped;
		if(!stop)
		{
			try 
			{

				macroMethods = new MacroMethods();

				
				//kill all acrobat in memory to work in a clean way
				macroMethods.killAcrobat();
				
				
				
				
				///// read stamps sizes and change parameters

				logger.info("loading stamp file");
				//copy of stamp to put new parameters
				File stamp = new File("../Temp/"+job.getPDFStamp().getName());
				ServiceProcess.copy(job.getPDFStamp(), stamp);
				
				//open stamp PDF
				logger.info("opening stamp file");
				macroMethods.openPdf(stamp);
				macroMethods.sendFullScreen();

				
				
				//get last page number
				logger.info("getting stamp last page");
				macroMethods.screenCapture(null, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"___(screenCaptureGetLastPageNumber).jpg"));
				stampleLastPageNumber=Integer.parseInt(macroMethods.getLastPageNumber());

				
				/////change parameters
				logger.info("change parameters");
				for (int currentStamplePage = 1; currentStamplePage <= stampleLastPageNumber-1; currentStamplePage++)
				{
					String information=macroMethods.getTextInsideStample(currentStamplePage);
					//change parameters
					information=information.replace("##Parameter1##", job.getParameter1());
					information=information.replace("##Parameter2##", job.getParameter2());
					information=information.replace("##Parameter3##", job.getParameter3());
					information=information.replace("##Parameter4##", job.getParameter4());
					information=information.replace("##Parameter5##", job.getParameter5());
					macroMethods.setTextInsideStample(currentStamplePage, information);					
				}				
				//save modifications
				logger.info("save stamp file");
				macroMethods.sendSave();
				
				macroMethods.screenCapture(null, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"___(screenCaptureGotoLastPageNumber).jpg"));
				macroMethods.goToPageNumber(stampleLastPageNumber);		

				////read stamp size
				macroMethods.sendFormMode();
				//take a screenshot of the last page (empty)
				white = macroMethods.screenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()), new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"___(screenCaptureBlankPage).jpg")); //image of an empty page
				//dimension of the page
				Rectangle dim = ServiceProcess.dimension(white);
				white=white.getSubimage((int)dim.getX(), (int)dim.getY(), (int)dim.getWidth(), (int)dim.getHeight());
				logger.info("dimension of a page : X : " + dim.getX() + " | Y : " + dim.getY() + " | width : " + dim.getWidth() + " | height : " + dim.getHeight());
				
				logger.info("reading stamp sizes");
				for (int currentStamplePage = 1; currentStamplePage <= stampleLastPageNumber-1; currentStamplePage++)
				{
					macroMethods.goToPageNumber(currentStamplePage);
					
					//read stamp size
					stampList.add(new Stamp(ServiceProcess.compareImages(white, macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentStamplePage+"_(screenCapture).jpg")), new File("Image/"+job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentStamplePage+"_(compareForStampSize).jpg"))));
				}
				logger.info("closing stamp file");
				macroMethods.sendCloseWindow();

				//kill all acrobat in memory to work in a clean way
				macroMethods.killAcrobat();

				
				
				
				
				//////search last page of pdf
				//copy the PDF file
				logger.info("copying the pdf file");
				File pdfSource = new File(ServiceConfig.destination.getPath() + "\\" + job.getPDFSource().getName());
				ServiceProcess.copy(job.getPDFSource(), pdfSource);

				logger.info("opening the destination pdf file");
				macroMethods.openPdf(pdfSource);
				macroMethods.sendFullScreen();
				
				//open source pdf
				//get last page number
				logger.info("checking last page of pdf");
				if(job.getLastPage()==null)
				{
					macroMethods.screenCapture(null, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"___(screenCaptureGetLastPageNumber).jpg"));
					lastPageNumber =  Integer.parseInt(macroMethods.getLastPageNumber());
				}
				else
				{
					macroMethods.screenCapture(null, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"___(screenCaptureGetLastPageNumber).jpg"));
					lastPageNumber = job.getLastPage();
					int check = Integer.parseInt(macroMethods.getLastPageNumber());
					if(lastPageNumber > check)
					{
						logger.warn("last page is superior to number of page");
						lastPageNumber = check;
					}
				}
				logger.info("last page : " + lastPageNumber);

				macroMethods.sendCloseWindow();

				//kill all acrobat in memory to work in a clean way

				
				
				
				
				///////Stamping

				currentPage = firstPageNumber;
				//ServiceController sc = new ServiceController();
				stop = ServiceController.isStop() || Run.stopped;
				boolean rotatePageMode=false;	//is in rotate page mode
				
				while(currentPage <= lastPageNumber && !stop)
				{
					logger.info("page " + currentPage);
					try
					{

					if (!pdfStampleIsOpen)
					{
						logger.info("loading first stamp");
						////load first stample in clipboard
						macroMethods.openPdf(stamp);
						macroMethods.sendFullScreen();	
						macroMethods.goToPageNumber(1);					
						macroMethods.sendFormMode();
						macroMethods.sendSelectAll();
						macroMethods.sendCopy();
						pdfStampleIsOpen=true;
						stamplePageInClipboard=1;
					}

					
					if (!pdfIsOpen)
					{
						logger.info("loading pdf");

						macroMethods.openPdf(pdfSource);
						if (rotatePageMode)
						{
							macroMethods.sendLeftRotation();
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterLeftRotation).jpg"));
						}
						macroMethods.sendFullScreen();
						macroMethods.sendFormMode();
						pdfIsOpen=true;
					}
						
					macroMethods.goToPageNumber(currentPage);
					
					boolean stampleInsertionProcessEnd=false;
					
					do {
						
						screen = macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureForSearchProcess).jpg"));

						int x = 0, y = 0;
						//checking each stamp while not found a place
						int currentStamplePage = 1;
						stamplePlaceFound=false;
						int bestPlaceX = 0;
						int bestPlaceY = 0;
						int bestPlaceStamp = 1;
						double bestPlaceVal = 0;
						stop = ServiceController.isStop() || Run.stopped;
						while(currentStamplePage <= stampleLastPageNumber-1 && !stamplePlaceFound && !stop)
						{
							logger.info("nbTryOnPage:"+nbTryOnPage);

							nbTryOnPage++;
							if (nbTryOnPage==5){
								//infinite loop first time -> override retry mode to false to be sure a retry will occur
								retryMode=false;
								throw new ErrorException("78 : infinite loop first time", 1, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenInfiniteLoopFirstTime).jpg"));
							}
							if (nbTryOnPage>10){
								//infinite loop in retry mode -> override retry mode to true to be sure to go to next page
								retryMode=true;
								throw new ErrorException("79 : infinite loop in retry", 1, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenInfiniteLoopInRetry).jpg"));
							}							
							
							ColorModel cm = screen.getColorModel();
							BufferedImage searchLog = new BufferedImage(cm, screen.copyData(null), cm.isAlphaPremultiplied(), null);
							int width = (int) stampList.get(currentStamplePage-1).getR().getWidth();
							int height = (int) stampList.get(currentStamplePage-1).getR().getHeight();
							//check
							y = screen.getHeight() - height - marge;
							stop = ServiceController.isStop() || Run.stopped;
							while(y > marge && !stamplePlaceFound && !stop)
							{
									
								x = screen.getWidth() - width;
								while(x > 0 && !stamplePlaceFound && !stop)
								{
									double res = ServiceProcess.isFree(screen, new Rectangle(x, y, width, height));
									stamplePlaceFound = res == 1;
									if(res>bestPlaceVal)
									{
										bestPlaceVal = res;
										bestPlaceX = x;
										bestPlaceY = y;
										bestPlaceStamp = currentStamplePage;
									}
									if (!stamplePlaceFound)
									{
								        for(int i = x; i < x+width; i=i+7)
								        {
								        	searchLog.setRGB(i, y, 0xff0000);
								        	searchLog.setRGB(i, y+height-1, 0xff0000);
								        }
								        //Draw Columns of rectangle
								        for(int i = y; i < y+height; i=i+7)
								        {
								        	searchLog.setRGB(x, i, 0xff0000);
								        	searchLog.setRGB(x+width-1, i, 0xff0000);
								       }
								        if (res==0){
								        	x -= width;
								        } 
								        else
								        {
									        if (res<0.5f){
									        	x -= width/2;
									        } 
									        else
									        {
									        	x -= vstep;
									        }
								        }
									}
									else
									{
								        for(int i = x; i < x+width; i++)
								        {
								        	searchLog.setRGB(i, y, 0xff00);
								        	searchLog.setRGB(i, y+height-1, 0xff00);
								        }
								        //Draw Columns of rectangle
								        for(int i = y; i < y+height; i++)
								        {
								        	searchLog.setRGB(x, i, 0xff00);
								        	searchLog.setRGB(x+width-1, i, 0xff00);
								        }
									}									
								}
								if (!stamplePlaceFound)
								{
									y -= hstep;
								}
							}
							if (!stamplePlaceFound)
							{
								currentStamplePage ++;
								logger.info("no place found; using next stample "+ currentStamplePage);

							}
	
					        try 
					        {
					            ImageIO.write(searchLog, "jpg", new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenLogForSearch).jpg"));  // ignore returned boolean
					        } catch(IOException e) 
					        {
								throw new ErrorException("08 : "+e.getMessage(), 1);
					        	//JOptionPane.showMessageDialog(null, "Can't save the log picture", "Error : screenshot", JOptionPane.WARNING_MESSAGE);
					        }
					        stop = ServiceController.isStop() || Run.stopped;
						}
						
						if (!stamplePlaceFound)
						{
							x=bestPlaceX;
							y=bestPlaceY;
							currentStamplePage=bestPlaceStamp;
							logger.info("no place found; using best place and stample "+ currentStamplePage);
						}
	
						if (currentStamplePage!=stamplePageInClipboard)
						{
							//close PDF and load the needed stample in clipboard
							logger.info("reload stample "+ currentStamplePage);

							macroMethods.sendSave();
							macroMethods.sendCloseWindow();
		
							macroMethods.screenCapture(null, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"___(screenCaptureGotoLastPageNumber).jpg"));
							macroMethods.goToPageNumber(currentStamplePage);
							
							macroMethods.sendSelectAll();
							macroMethods.sendCopy();						
							stamplePageInClipboard=currentStamplePage;
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterChangeStample).jpg"));
			
							//reload PDF and go back to the current page and the correct rotation
							macroMethods.openPdf(pdfSource);
							if (rotatePageMode){
								macroMethods.sendLeftRotation();
								macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterLeftRotation).jpg"));
							}
							macroMethods.sendFullScreen();
							macroMethods.sendFormMode();
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterReloadAfterStampleCopy).jpg"));
							macroMethods.goToPageNumber(currentPage);
							
						}
						BufferedImage before = macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureBeforePaste).jpg"));
						macroMethods.sendPaste();
						Rectangle position = ServiceProcess.compareImages(macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterPaste).jpg")), before, new File("Image/"+job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_"+"_(CompareImagesAfterPaste).jpg"));
						double ratioInsertedStample=position.getWidth()/position.getHeight();
						double ratioOriginalStample=stampList.get(currentStamplePage-1).getR().getWidth()/stampList.get(currentStamplePage-1).getR().getHeight();
						if (Math.abs(ratioInsertedStample-ratioOriginalStample)>1.5){
							logger.info("wrong page rotation " +Math.abs(ratioInsertedStample-ratioOriginalStample));
							macroMethods.sendUndo();
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterReloadBeforeRemiseEnPosition).jpg"));
							macroMethods.sendSave();
							macroMethods.sendCloseWindow();
							rotatePageMode=!rotatePageMode;
							//reload PDF and go back to the current page and the correct rotation
							macroMethods.openPdf(pdfSource);
							if (rotatePageMode)
							{
								macroMethods.sendLeftRotation();
								macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterLeftRotation).jpg"));
							}
							macroMethods.sendFullScreen();
							macroMethods.sendFormMode();
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterReloadAfterPaegRotation).jpg"));
							macroMethods.goToPageNumber(currentPage);
						}
						else
						{
							macroMethods.move( (int) position.getCenterX()+ (int) dim.getX(), (int) position.getCenterY()+ (int) dim.getY(), x + (int) dim.getX()+ (int) stampList.get(currentStamplePage-1).getR().getWidth() / 2, y + (int) dim.getY()+ (int) stampList.get(currentStamplePage-1).getR().getHeight() / 2);
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterReloadAfterMove).jpg"));
							stampleInsertionProcessEnd=true;
						}

					} while (!stampleInsertionProcessEnd);
										
					//check if it's the correct page
					if (currentPage!=Integer.parseInt(macroMethods.getCurrentPageNumber())){
						throw new ErrorException("77 : wrong page number", 1, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterMove).jpg"));
					}
					
					//save modifications					
					macroMethods.sendSave();
					macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+currentPage+".jpg"));
					currentPage++;	
					pageProcessCount++;
					job.getResult().setPageProcessCount(pageProcessCount);
					job.getResult().setEndJobDateTime(new Date());
					job.getResult().save();
					retryMode=false;
					nbTryOnPage=0;
					if ((currentPage % 5)==0){
						logger.info("close accrobat every 5 pages");
						macroMethods.sendSave();
						macroMethods.sendCloseWindow();
						macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterClosePdf).jpg"));
						macroMethods.sendSave();
						macroMethods.sendCloseWindow();
						macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterCloseStamp).jpg"));
						macroMethods.killAcrobat();
						pdfStampleIsOpen=false;
						pdfIsOpen=false;
						macroMethods.waitLowProcessorWithDelay();
						}					
					}
					catch(ErrorException e)
					{
						//kill all acrobat in memory to start in a clean way
						logger.info("exception");
						logger.info(e.toString());
						macroMethods.killAcrobat();
						pdfStampleIsOpen=false;
						pdfIsOpen=false;
						if (!retryMode)
						{
							logger.info("retry after exception");
							retryMode=true;
						}
						else
						{
							pageErrorCount++;
							job.getResult().setPageErrorCount(pageErrorCount);
							if(e.isFile_Exist())
							{
								job.getResult().addError(currentPage, e.getMessage(), e.getLevel(), e.getFile());
							}
							else
							{
								job.getResult().addError(currentPage, e.getMessage(), e.getLevel());
							}
							
							//error, but do the next page
							pageProcessCount++;
							macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+currentPage+".jpg"));
							currentPage++;			
							job.getResult().setPageProcessCount(pageProcessCount);
							job.getResult().setEndJobDateTime(new Date());
							job.getResult().save();
							retryMode=false;
							nbTryOnPage=0;
						}
					}

				}
				
				//close pdf and stample
				macroMethods.sendSave();
				macroMethods.sendCloseWindow();
				macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterClosePdf).jpg"));
				macroMethods.sendSave();
				macroMethods.sendCloseWindow();
				macroMethods.screenCapture(dim, new File(ServiceConfig.image.getPath() + "\\" + job.getPDFSource().getName()+"_"+new Date().toString().replace(":","_")+"_"+currentPage+"_(screenCaptureAfterCloseStamp).jpg"));
				
			}
			catch (ErrorException e)
			{
				logger.info("exception");
				logger.info(e.toString());
				if(e.isFile_Exist())
				{
					job.getResult().addError(currentPage, e.getMessage(), e.getLevel(), e.getFile());
				}
				else
				{
					job.getResult().addError(currentPage, e.getMessage(), e.getLevel());
				}
				job.getResult().setGeneralError(e.getMessage());
				job.getResult().setEndJobDateTime(new Date());
				job.getResult().save();
			}
			
			//set and save the result	
			job.getResult().save();
		}
	}
	
	
	
	/**
	 * return the value of stop
	 * @return
	 */
	public boolean isStop() 
	{
		return stop;
	}

	/**
	 * set the value of stop
	 * @param stop
	 */
	public void setStop(boolean stop)
	{
		this.stop = stop;

	}

}
