import java.awt.AWTException;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
/**
 * Contain macro to access to the PDF
 * @author JPR
 *
 */
public class MacroMethods 
{
	static Logger logger = Logger.getLogger(MacroMethods.class.getName());

	private static Robot robot;	//to send macros

	/**
	 * Constructor
	 * @throws AWTException
	 */
	public MacroMethods() throws ErrorException 
	{ 
		try
		{
		robot = new Robot();
	}
		catch (AWTException e)
		{
			throw new ErrorException("01 : "+e.getMessage(), 1);
		}
	}

	/**
	 * send a key char to the PDF
	 * @param shift true to send shift is pressed
	 * @param ctrl true to send control is pressed
	 * @param key the key char to send
	 */
	public void sendKeys(boolean alt, boolean shift, boolean ctrl, char key, boolean fast) throws ErrorException
	{
		logger.info("sendKeys:"+key);
		if (alt)
		{
	        robot.keyPress(KeyEvent.VK_ALT); 
		}
		if (shift)
		{
	        robot.keyPress(KeyEvent.VK_SHIFT); 
		}
		if (ctrl)
		{
			robot.keyPress(KeyEvent.VK_CONTROL);
		}
		int ascii = (int) key;
		robot.keyPress(ascii);
		waitLowProcessor(true);
		robot.keyRelease(ascii);
		if (ctrl)
		{
			robot.keyRelease(KeyEvent.VK_CONTROL);
		}
		if (shift)
		{
	        robot.keyRelease(KeyEvent.VK_SHIFT); 
		}
		if (alt)
		{
	        robot.keyRelease(KeyEvent.VK_ALT); 
		}
		waitLowProcessor(fast);
	}

	/**
	 * send a key int to the PDF
	 * @param shift true to send shift is pressed
	 * @param ctrl true to send control is pressed
	 * @param key the key char to send
	 */
	public void sendKeys(boolean alt, boolean shift, boolean ctrl, int key, boolean fast) throws ErrorException
	{
		logger.info("sendKeys:"+Integer.toString(key));
		if (alt)
		{
	        robot.keyPress(KeyEvent.VK_ALT); 
		}
		if (shift)
		{
	        robot.keyPress(KeyEvent.VK_SHIFT); 
		}
		if (ctrl)
		{
			robot.keyPress(KeyEvent.VK_CONTROL);
		}

		robot.keyPress(key);
		waitLowProcessor(true);
		robot.keyRelease(key);
		if (ctrl)
		{
			robot.keyRelease(KeyEvent.VK_CONTROL);
		}
		if (shift)
		{
	        robot.keyRelease(KeyEvent.VK_SHIFT); 
		}
		if (alt)
		{
	        robot.keyRelease(KeyEvent.VK_ALT); 
		}
		waitLowProcessor(fast);
	}

	/**
	 * send the keys to put the PDF in full screen
	 */
	public void sendFullScreen() throws ErrorException
	{
		logger.info("sendFullScreen");
		//maximize
		sendKeys (false,false,true, 'L',false);
		//zoom 50%
		sendKeys (false, false, true, 'Y',false);
		sendString ("50");
		sendEnter();
		//single page view
		sendKeys (true, false, false, 'V',false);
		sendKeys (false, false, false, 'P',false);
		sendKeys (false, false, false, 'S',false);
	}

	/**
	 * send keys to open the "Goto page" popup
	 */
	public void sendGotoPage() throws ErrorException
	{
		logger.info("sendGotoPage");
		sendKeys (false, true, true, 'N',false);
	}


	/**
	 * send keys to go to last page
	 */
	public void sendGotoLastPage() throws ErrorException
	{
		logger.info("sendGotoLastPage");
		sendKeys(false, false, false, KeyEvent.VK_END,false);
	}

	/**
	 * send keys to Escape a popup
	 */
	public void sendEsc() throws ErrorException
	{
		logger.info("sendEsc");
		sendKeys(false, false, false, KeyEvent.VK_ESCAPE,false);
	}

	/**
	 * send keys to do a paste
	 */
	public void sendPaste() throws ErrorException
	{
		logger.info("sendPaste");
		sendKeys (false, false, true, 'V',false);
	}
	

	/**
	 * send keys to do an undo
	 */
	public void sendUndo() throws ErrorException
	{
		logger.info("sendUndo");
		sendKeys (false, false, true, 'Z',false);
	}
	
	/**
	 * send keys to do a left rotation
	 */
	public void sendLeftRotation() throws ErrorException
	{
		logger.info("sendLeftRotation");
		sendKeys (true, false, false, 'V',false);
		sendKeys (false, false, false, 'V',false);
		sendKeys (false, false, false, 'c',false);
	}
	
	/**
	 * send keys to do a right rotation
	 */
	public void sendRightRotation() throws ErrorException
	{
		logger.info("sendRightRotation");
		sendKeys (true, false, false, 'V',false);
		sendKeys (false, false, false, 'V',false);
		sendKeys (false, false, false, 'w',false);
	}

	/**
	 * send keys to do a select all
	 */
	public void sendSelectAll() throws ErrorException
	{
		logger.info("sendSelectAll");
		sendKeys (false, false, true, 'A',false);
	}
	
	/**
	 * send keys to close a window
	 */
	public void sendCloseWindow() throws ErrorException
	{
		logger.info("");
		sendKeys (true, false, false, KeyEvent.VK_F4,false);
	}

	
	/**
	 * send tab key
	 */
	public void sendTab() throws ErrorException
	{
		logger.info("");
		sendKeys (false, false, false, KeyEvent.VK_TAB,false);
	}
	
	/**
	 * minimize
	 */
	public void setApplicationToMinimized(Frame frame)
	{
		frame.setState(Frame.ICONIFIED);
	}

	/**
	 * normal
	 */
	public void setApplicationToNormal(Frame frame)
	{
		frame.setState(Frame.NORMAL);
	}

	/**
	 * send keys to switch to form mode
	 */
	public void sendFormMode() throws ErrorException
	{
		logger.info("sendFormMode");
		sendKeys (false, true, true, KeyEvent.VK_NUMPAD7,false);
		sendEsc();
	}

	/**
	 * send keys to save
	 */
	public void sendSave() throws ErrorException
	{
		logger.info("sendSave");
		sendKeys (false,false, true, 'S',false);
		waitLowProcessorWithDelay();
	}

	/**
	 * send keys to do a copy
	 */
	public void sendCopy() throws ErrorException
	{
		logger.info("sendCopy");
		sendKeys (false, false, true, 'C',false);
	}
	
	/**1
	 * 
	 * save a capture of the screen
	 * @throws AWTException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	  public BufferedImage screenCapture(Rectangle dim, File file) throws ErrorException 
	  {
		logger.info("screenCapture");
		try
		{
		BufferedImage screencapture; 
		if (dim==null){
			screencapture = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()) );
		} 
		else
		{
			screencapture = new Robot().createScreenCapture(dim);
		}
		ImageIO.write(screencapture, "jpg", file);
		return screencapture;
	  }
		catch (AWTException | IOException e)
		{
			throw new ErrorException("02 : "+e.getMessage(), 1);	
		}
	  }

	  /**
	   * used to send the number of a page
	   * @param string number of the page
	   */
	public void sendString(String string) throws ErrorException
	{
		logger.info("sendString:"+string);
		for (int i = 0; i < string.length(); i++)
		{
			switch (string.charAt(i)) 
			{
			case '0':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD0, true);
				break;

			case '1':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD1, true);
				break;

			case '2':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD2, true);
				break;

			case '3':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD3, true);
				break;

			case '4':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD4, true);
				break;

			case '5':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD5, true);
				break;

			case '6':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD6, true);
				break;

			case '7':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD7, true);
				break;

			case '8':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD8, true);
				break;

			case '9':
				sendKeys(false,false, false, KeyEvent.VK_NUMPAD9, true);
				break;
			}
		}
		waitLowProcessor(false);	
	}	
	
	/**
	 * send the key enter
	 */
	public void sendEnter() throws ErrorException
	{
		logger.info("sendEnter");
		robot.keyPress(10);
		waitLowProcessor(true);	
		robot.keyRelease(10);
		waitLowProcessor(false);	
	}
	
	/**
	 * change to the page number passed in parameter
	 * @param pageNumber
	 * @throws AWTException
	 */
	public void goToPageNumber(Integer pageNumber) throws ErrorException
	{
		logger.info("gotoPageNumber:"+Integer.toString(pageNumber));
		sendGotoPage();
		sendString(Integer.toString(pageNumber));
		sendEnter();
	}
	
	/**
	 * go to last page, and get it's page number
	 * @throws AWTException
	 * @throws IOException 
	 * @throws UnsupportedFlavorException 
	 */
	public String getLastPageNumber() throws ErrorException 
	{
		logger.info("getLastPageNumber");
		sendGotoLastPage();
		return getCurrentPageNumber();
	}


	/**
	 * go to last page, and get it's page number
	 * @throws AWTException
	 * @throws IOException 
	 * @throws UnsupportedFlavorException 
	 */
	public String getCurrentPageNumber() throws ErrorException 
	{
		logger.info("getPageNumber");
		ServiceClipboard serviceClipboard=new ServiceClipboard();
		serviceClipboard.setClipboardContents("0");
		String result;
		int nbRead=0;
		do {
			sendGotoPage();
			sendCopy();
			result = (String) serviceClipboard.getClipboardContents();
			sendEsc();
			nbRead++;
		} while ((result==null || result.equalsIgnoreCase("0")) && nbRead<5);
		if (nbRead==5)
		{
			result="1";
			throw new ErrorException("79 : can't read clipboard content", 1);
		}
		return result;
	}
	
	/**
	 * get text inside stample
	 * @throws AWTException
	 * @throws IOException 
	 * @throws UnsupportedFlavorException 
	 */
	public String getTextInsideStample(int pageNumber) throws ErrorException 
	{
		logger.info("getTextInsideStample");
		ServiceClipboard serviceClipboard=new ServiceClipboard();
		serviceClipboard.setClipboardContents("-------");
		goToPageNumber(pageNumber);
		sendTab();
		sendSelectAll();
		String result;
		int nbRead=0;
		do {
			sendCopy();
			result = (String) serviceClipboard.getClipboardContents();
			nbRead++;
		} while ((result==null || result.equalsIgnoreCase("-------")) && nbRead<5);
		if (nbRead==5)
		{
			throw new ErrorException("80 : can't read clipboard content to get text inside stamp", 1);
		}
		return result;
	}

	
	/**
	 * get text inside stample
	 * @throws AWTException
	 * @throws IOException 
	 * @throws UnsupportedFlavorException 
	 */
	public void setTextInsideStample(int pageNumber, String text) throws ErrorException 
	{
		logger.info("setTextInsideStample");
		goToPageNumber(pageNumber);
		sendTab();
		sendSelectAll();
		ServiceClipboard serviceClipboard=new ServiceClipboard();
		serviceClipboard.setClipboardContents(text);
		waitLowProcessor(false);
		sendPaste();
	}


	public void openPdf2(File file)
    {
		logger.info("openPdf2");
        try
        {            
            String[] cmd = new String[3];
            cmd[0] = "cmd.exe" ;
            cmd[1] = "/C" ;
            cmd[2] = file.getAbsolutePath();
            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
            proc.waitFor();     
			waitLowProcessorWithDelay();
       }
        catch (Throwable t)
        {
        	t.printStackTrace();
        }
    }
	
	public void killAcrobat() throws ErrorException {

		logger.info("killAcrobat");
        try
        {            
            String[] cmd = new String[4];
            cmd[0] = "taskkill";
            cmd[1] = "/F";
            cmd[2] = "/IM";
            cmd[3] = "Acrobat.exe";
            
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
            proc.waitFor();    
			waitLowProcessorWithDelay();
       }
        catch (Throwable t)
        {
        	t.printStackTrace();
        }
   }

	
	/**
	 * open the PDF corresponding to the File in parameter
	 * @param job
	 */
	public Process openPdf(File file)
	{
		logger.info("openPdf: "+file.getAbsolutePath());
		Process p = null;
		
		try
		{
			String fileName = file.getAbsolutePath();
			if ((new File(fileName)).exists()) {
				p=Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + fileName);
			} else {
				logger.info("File is not exists");
			}
			Thread.sleep(3000L);
			waitLowProcessorWithDelay();
			
			//send enter in case of an recovery error
			sendEnter();
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
		}
		return p;
	}

	/**
	 * destroy the process
	 * @param process
	 */
	public void destroyProcess(Process process)
	{
		process.destroy();
	}


	/**
	 * wait until the pdf is opened or saved
	 */
	public void waitLowProcessorWithDelay() throws ErrorException
	{
		waitLowProcessor(false, 10);
	}
	
		
	/**
	 * wait while the page is not loaded
	 */
	public void waitLowProcessor(boolean fastWait) throws ErrorException
	{
		waitLowProcessor(fastWait,3);
	}
	

	/**
	 * wait while the page is not loaded
	 */
	public void waitLowProcessor(boolean fastWait, int testLenght) throws ErrorException
	{
		try 
		{
			if (fastWait){
				Thread.sleep(10L);				
			}
			else
			{
				
				logger.info("waitLowProcessor");
				int cpttry = 0;
				int processorNotWorkingCount=0;

				do {
				
					Thread.sleep(10L);				
					cpttry++;
					if(cpttry == 250){
						throw new ErrorException("04 : processor is too busy", 1);
					}

					CPUController cpuController= new CPUController();
					if (cpuController.getCpu() > 0){
						processorNotWorkingCount=0;
						Thread.sleep(20L);
						if((cpttry % 5)==0){
							robot.keyPress(KeyEvent.VK_CAPS_LOCK); 
							robot.keyRelease(KeyEvent.VK_CAPS_LOCK); 
							Thread.sleep(50L);
							robot.keyPress(KeyEvent.VK_CAPS_LOCK); 
							robot.keyRelease(KeyEvent.VK_CAPS_LOCK);
						}
					}
					else
					{
						processorNotWorkingCount++;
					}
				} while (processorNotWorkingCount<testLenght);
				
			}
		}
		catch(InterruptedException e)
		{
			throw new ErrorException("05 : "+e.getMessage(), 1);
		}
	}
	
	
	/**
	 * move an object to the given position or select a zone
	 */
	public void move(int x1, int y1, int x2, int y2) throws ErrorException
	{
		logger.info("moveMouse");
		robot.mouseMove(x1, y1);
		waitLowProcessor(true);
		robot.mousePress(16);
		waitLowProcessor(false);
		robot.mouseMove(x2, y2);
		waitLowProcessor(true);
		robot.mouseRelease(16);
		waitLowProcessor(false);
	}

	/**
	 * Select an object at the given position
	 */
	public void select(int x, int y) throws ErrorException
	{
		logger.info("selectMouse");
		robot.mouseMove(x, y);
		waitLowProcessor(true);
		robot.mousePress(16);
		waitLowProcessor(false);
		robot.mouseRelease(16);
		waitLowProcessor(false);
	}

}