package test.com.zebra.sdk.job.examples;

import java.awt.Color;
import java.io.*;
import java.util.*;

import org.apache.commons.io.FileUtils;

import com.zebra.sdk.comm.*;
import com.zebra.sdk.common.card.containers.*;
import com.zebra.sdk.common.card.enumerations.CardSide;
import com.zebra.sdk.common.card.enumerations.*;
import com.zebra.sdk.common.card.exceptions.ZebraCardException;
import com.zebra.sdk.common.card.graphics.*;
import com.zebra.sdk.common.card.graphics.enumerations.RotationType;
import com.zebra.sdk.common.card.jobSettings.ZebraCardJobSettingNames;
import com.zebra.sdk.common.card.printer.*;
import com.zebra.sdk.device.ZebraIllegalArgumentException;

public class MagneticEncodeAndPrintExample {

	public static void main(String[] args) {
		Connection connection = null;
		ZebraCardPrinter zebraCardPrinter = null;

		try {
			connection = new TcpConnection("1.2.3.4", 9100);
			connection.open();

			zebraCardPrinter = ZebraCardPrinterFactory.getInstance(connection);

			if (zebraCardPrinter.hasMagneticEncoder()) {
				List<GraphicsInfo> graphicsData = drawGraphics(zebraCardPrinter);

				// Configure magnetic encoding settings
				zebraCardPrinter.setJobSetting(ZebraCardJobSettingNames.MAG_ENCODING_TYPE, "ISO"); // ISO=default
				zebraCardPrinter.setJobSetting(ZebraCardJobSettingNames.MAG_COERCIVITY, "High"); // High=default
				zebraCardPrinter.setJobSetting(ZebraCardJobSettingNames.MAG_VERIFY, "yes"); // yes=default

				// Send job
				int jobId = zebraCardPrinter.printAndMagEncode(1, graphicsData, "Zebra Technologies", "2222222222", "3333333333");

				// Poll job status
				JobStatusInfo jobStatusInfo = pollJobStatus(jobId, zebraCardPrinter);
				System.out.println(String.format(Locale.US, "Job %d completed with status '%s'", jobId, jobStatusInfo.printStatus));
			} else {
				System.out.println("No magnetic encoder installed.");
			}
		} catch (Exception e) {
			System.out.println("Error printing and encoding card: " + e.getMessage());
		} finally {
			cleanUpQuietly(connection, zebraCardPrinter);
		}
	}

	private static List<GraphicsInfo> drawGraphics(ZebraCardPrinter zebraCardPrinter) throws ConnectionException, IllegalArgumentException, IOException, ZebraCardException {
		List<GraphicsInfo> graphicsData = new ArrayList<GraphicsInfo>();
		ZebraGraphics graphics = null;

		try {
			graphics = new ZebraCardGraphics(zebraCardPrinter);

			// Front side color
			String colorImagePath = "path/to/myColorImage.bmp";
			byte[] imageData = FileUtils.readFileToByteArray(new File(colorImagePath));

			ZebraCardImageI zebraCardImage = drawImage(graphics, PrintType.Color, imageData, 0, 0, 0, 0);
			graphicsData.add(addImage(CardSide.Front, PrintType.Color, 0, 0, -1, zebraCardImage));
			graphics.clear();

			// Front side full overlay
			graphicsData.add(addImage(CardSide.Front, PrintType.Overlay, 0, 0, 1, null));

			// Back side mono
			String monoImagePath = "path/to/myMonoImage.bmp";
			imageData = FileUtils.readFileToByteArray(new File(monoImagePath));

			zebraCardImage = drawImage(graphics, PrintType.MonoK, imageData, 0, 0, 0, 0);
			graphicsData.add(addImage(CardSide.Back, PrintType.MonoK, 0, 0, -1, zebraCardImage));
		} finally {
			if (graphics != null) {
				graphics.close();
			}
		}
		return graphicsData;
	}

	private static ZebraCardImageI drawImage(ZebraGraphics graphics, PrintType printType, byte[] imageData, int xOffset, int yOffset, int width, int height) throws IllegalArgumentException, IOException {
		graphics.initialize(0, 0, OrientationType.Landscape, printType, Color.WHITE);
		graphics.drawImage(imageData, xOffset, yOffset, width, height, RotationType.RotateNoneFlipNone);
		return graphics.createImage();
	}

	private static GraphicsInfo addImage(CardSide side, PrintType printType, int xOffset, int yOffset, int fillColor, ZebraCardImageI zebraCardImage) {
		GraphicsInfo graphicsInfo = new GraphicsInfo();
		graphicsInfo.fillColor = fillColor;
		graphicsInfo.graphicData = zebraCardImage != null ? zebraCardImage : null;
		graphicsInfo.graphicType = zebraCardImage != null ? GraphicType.BMP : GraphicType.NA;
		graphicsInfo.opacity = 0;
		graphicsInfo.overprint = false;
		graphicsInfo.printType = printType;
		graphicsInfo.side = side;
		graphicsInfo.xOffset = xOffset;
		graphicsInfo.yOffset = yOffset;
		return graphicsInfo;
	}

	private static JobStatusInfo pollJobStatus(int jobId, ZebraCardPrinter zebraCardPrinter) throws ConnectionException, ZebraCardException, ZebraIllegalArgumentException {
		long dropDeadTime = System.currentTimeMillis() + 40000;
		long pollInterval = 500;

		// Poll job status
		JobStatusInfo jobStatusInfo = new JobStatusInfo();

		do {
			jobStatusInfo = zebraCardPrinter.getJobStatus(jobId);

			String alarmDesc = jobStatusInfo.alarmInfo.value > 0 ? String.format(Locale.US, " (%s)", jobStatusInfo.alarmInfo.description) : "";
			String errorDesc = jobStatusInfo.errorInfo.value > 0 ? String.format(Locale.US, " (%s)", jobStatusInfo.errorInfo.description) : "";

			System.out.println(String.format("Job %d, Status:%s, Card Position:%s, Mag Status:%s, Alarm Code:%d%s, Error Code:%d%s", jobId, jobStatusInfo.printStatus, 
					jobStatusInfo.cardPosition, jobStatusInfo.magneticEncoding, jobStatusInfo.alarmInfo.value, alarmDesc, jobStatusInfo.errorInfo.value, errorDesc));

			if (jobStatusInfo.printStatus.contains("done_ok")) {
				break;
			} else if (jobStatusInfo.printStatus.contains("alarm_handling")) {
				System.out.println("Alarm Detected: " + jobStatusInfo.alarmInfo.description);
			} else if (jobStatusInfo.printStatus.contains("error") || jobStatusInfo.printStatus.contains("cancelled")) {
				break;
			} else if (jobStatusInfo.errorInfo.value > 0) {
				System.out.println(String.format(Locale.US, "The job encountered an error [%s] and was cancelled.", jobStatusInfo.errorInfo.description));
				zebraCardPrinter.cancel(jobId);
			}

			if (System.currentTimeMillis() > dropDeadTime) {
				break;
			}

			try {
				Thread.sleep(pollInterval);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

		} while (true);

		return jobStatusInfo;
	}

	private static void cleanUpQuietly(Connection connection, ZebraCardPrinter genericPrinter) {
		try {
			if (genericPrinter != null) {
				genericPrinter.destroy();
				genericPrinter = null;
			}
		} catch (ZebraCardException e) {
			e.printStackTrace();
		}

		if (connection != null) {
			try {
				connection.close();
				connection = null;
			} catch (ConnectionException e) {
				e.printStackTrace();
			}
		}
	}
}