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;

/**
 * Contain macro to access to the PDF
 * 
 * @author JPR
 *
 */
public class MacroMethods
{

	private 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) throws ErrorException
	{
		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);
		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();
	}

	/**
	 * 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) throws ErrorException
	{
		if (alt)
		{
			robot.keyPress(KeyEvent.VK_ALT);
		}
		if (shift)
		{
			robot.keyPress(KeyEvent.VK_SHIFT);
		}
		if (ctrl)
		{
			robot.keyPress(KeyEvent.VK_CONTROL);
		}

		robot.keyPress(key);
		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();
	}

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

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

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

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

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

	/**
	 * send keys to do an undo
	 */
	public void sendUndo() throws ErrorException
	{
		sendKeys(false, false, true, 'Z');
	}

	/**
	 * send keys to do a left rotation
	 */
	public void sendLeftRotation() throws ErrorException
	{
		sendRightRotation();
		sendRightRotation();
		sendRightRotation();
	}

	/**
	 * send keys to do a right rotation
	 */
	public void sendRightRotation() throws ErrorException
	{
		sendKeys(false, true, true, KeyEvent.VK_EXCLAMATION_MARK);
	}

	/**
	 * send keys to do a select all
	 */
	public void sendSelectAll() throws ErrorException
	{
		sendKeys(false, false, true, 'A');
	}

	/**
	 * send keys to close a window
	 */
	public void sendCloseWindow() throws ErrorException
	{
		sendKeys(true, false, false, KeyEvent.VK_F4);
		waitLowProcessor();
		waitLowProcessor();
		waitLowProcessor();
	}

	/**
	 * send tab key
	 */
	public void sendTab() throws ErrorException
	{
		sendKeys(false, false, false, KeyEvent.VK_TAB);
	}

	/**
	 * 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
	{
		sendKeys(false, true, true, KeyEvent.VK_NUMPAD7);
		sendEsc();
	}

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

	/**
	 * send keys to do a copy
	 */
	public void sendCopy() throws ErrorException
	{
		waitLowProcessor();
		sendKeys(false, false, true, 'C');
	}

	/**
	 * 1
	 * 
	 * save a capture of the screen
	 * 
	 * @throws AWTException
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public BufferedImage screenCapture(Rectangle dim, File file) throws ErrorException
	{
		try
		{

			waitLowProcessor();
			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
	{
		for (int i = 0; i < string.length(); i++)
		{
			waitLowProcessor();
			switch (string.charAt(i))
			{
			case '0':
				sendKeys(false, false, false, KeyEvent.VK_NUMPAD0);
				break;

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

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

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

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

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

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

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

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

			case '9':
				sendKeys(false, false, false, KeyEvent.VK_NUMPAD9);
				break;
			}
		}
		waitLowProcessor();
	}

	/**
	 * send the key enter
	 */
	public void sendEnter() throws ErrorException
	{
		robot.keyPress(10);
		robot.keyRelease(10);
		waitLowProcessor();
	}

	/**
	 * change to the page number passed in parameter
	 * 
	 * @param pageNumber
	 * @throws AWTException
	 */
	public void goToPageNumber(Integer pageNumber) throws ErrorException
	{
		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
	{
		sendGotoLastPage();
		return getCurrentPageNumber();
	}

	/**
	 * go to last page, and get it's page number
	 * 
	 * @throws AWTException
	 * @throws IOException
	 * @throws UnsupportedFlavorException
	 */
	public String getCurrentPageNumber() throws ErrorException
	{
		ServiceClipboard serviceClipboard = new ServiceClipboard();
		serviceClipboard.setClipboardContents("0");
		String result;
		int nbRead = 0;
		do
		{
			sendGotoPage();
			sendCopy();
			result = (String) serviceClipboard.getClipboardContents();
			sendEsc();
			nbRead++;
		}
		while (result.equalsIgnoreCase("0") && nbRead < 5);
		if (nbRead == 5)
		{
			result = "1";
			// TODO: faire une exception générale parce que impossible de lire le clipboard
		}
		return result;
	}

	/**
	 * open the PDF corresponding to the File in parameter
	 * 
	 * @param job
	 */
	public static void openPdf(File file)
	{
		String fileName = file.getAbsolutePath();
		if ((new File(fileName)).exists())
		{
			new Thread()
			{
				public void run()
				{
					try
					{
						Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + fileName);
					}
					catch (IOException e)
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} ;
		}
		else
		{
			System.out.println("File is not exists");
		}
	}

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

	/**
	 * wait while the page is not loaded
	 */
	public static void waitLowProcessor() throws ErrorException
	{
		float cpu[] = new float[10];
		do
		{
			int cpttry = 0;
			try
			{
				CPUController cc = new CPUController();
				for (int i = 0; i < 10; i++)
				{
					Thread.sleep(50L);
					cpu[i] = cc.cpuController(1);
				}
				cpttry++;
			}
			catch (InterruptedException e)
			{
				throw new ErrorException("05 : " + e.getMessage(), 1);
			}
			if (cpttry == 10)
				throw new ErrorException("04 : page chargement is too long", 1);
			// TODO si on attend plus de 10 minutes, alors killer le process accrobat et passer à la page suivante (destroyProcess)
			// cf process qui a ouvert le stamp et destroyProcess
		}
		while ((cpu[0] > 0.15f) || (cpu[1] > 0.15f) || (cpu[2] > 0.15f) || (cpu[3] > 0.15f) || (cpu[4] > 0.15f) || (cpu[5] > 0.15f) || (cpu[6] > 0.15f)
				|| (cpu[7] > 0.15f) || (cpu[8] > 0.15f) || (cpu[9] > 0.15f));
	}

	/**
	 * move an object to the given position or select a zone
	 */
	public void move(int x1, int y1, int x2, int y2) throws ErrorException
	{
		robot.mouseMove(x1, y1);
		waitLowProcessor();
		robot.mousePress(16);
		waitLowProcessor();
		robot.mouseMove(x2, y2);
		waitLowProcessor();
		robot.mouseRelease(16);
		waitLowProcessor();
	}

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

}