/*
 * Copyright (c) VAS "Latvijas Valsts radio un televīzijas centrs" (http://www.eparaksts.lv)
 * Developed by SIA "EUSO" (http://www.euso.lv)
 */
package lv.eparaksts.sample;

import java.io.*;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.*;

import lv.eparaksts.helpers.PdfQualifiedSignatureHelper;
import lv.eparaksts.pdf.*;
import lv.pasts.eme.security.*;
import lv.pasts.eme.smartcard.*;
import lv.pasts.eme.tsp.*;

/*
 * PDF sample application.
 * This example demonstrates how to create a signed PDF format document.
 *
 * Example opens PDF file 'args[0]',
 * signs PDF document if password is provided as a parameter 'args[2]'
 * and finally writes PDF document to file 'args[1]'.
 */
public class PdfSample implements CardAccessorCallback {

	private char[] password;

	static void printUsage(String[] args) {
		System.out.println("");
		for (int i = 0; i < args.length; i++) {
			System.out.println(args[i]);
		}

		System.out.println("");
		System.out.println("usage: java lv.eparaksts.sample.PdfSample pdfToOpen pdfToWrite [password]");
	}

	public static void main(String[] args) throws Exception {
		if (args.length != 3) {
			printUsage(args);
			//return;
		}

		//collect parameters
		String pdfToOpen = args[0];
		String pdfToWrite = args[1];
		char[] password = (args.length > 2) ? args[2].toCharArray() : null;

		//
		PdfDocument pdf = null;
		FileInputStream fileInputStream = null;
		FileOutputStream fileOutputStream = null;

		try {
			//open pdf
			System.out.println("Begin...");
			fileInputStream = new FileInputStream(pdfToOpen);
			pdf = new PdfDocument(fileInputStream);
			System.out.println("1 - PDF object created: pageCount=" + pdf.getPageCount());

			//check password
			if (password != null) {
				//prepare KeyAccessor for signature
				PdfSample cardAccessorCallback = new PdfSample();
				cardAccessorCallback.password = password;
				KeyAccessor keyAccessor = CardAccessor.getInstance(cardAccessorCallback);

				//prepare certificate
				String signingAlias = keyAccessor.selectDocumentSigningCertificate();
				X509Certificate certificate = keyAccessor.getCertificate(signingAlias);

				/*
				 * Signature PHASE 1 - prepare signature [PrivateKey IS NOT required]
				 */

				//prepare signature
				Map<String, String> signatureProperties = new HashMap<>();
				signatureProperties.put(PdfSignature.KEY_SIGNATURE_METHOD_OID, PdfSignature.SIGNATURE_METHOD_RSA_SHA256);
				PdfBasicSignature basicSignature = pdf.addSignature(signatureProperties);
				basicSignature.setLocation("Riga");
				basicSignature.setReason("PdfSample");
				basicSignature.setSigningCertificate(certificate);
				basicSignature.setVisualSignature(new PdfVisualSignature(1, 100, 100, 200, 100));
				System.out.println("2.1 - Signature prepared");

				//get signable data
				byte[] signable = basicSignature.getSignableBytes();
				String signatureMethodAlgorithm = basicSignature.getSignatureMethodAlgorithm();

				/*
				 * NOTE: At this moment PDF optionally can be stored to file
				 * and opened later to support server side load balance scenario.
				 */

				/*
				 * Signature PHASE 2 - calculate signature [PrivateKey IS required]
				 */

				//sign data, optionally use an external process
				Signature signature = Signature.getInstance(signatureMethodAlgorithm);
				signature.initSign(keyAccessor.getPrivateKey(signingAlias));
				signature.update(signable);
				byte[] signatureValue = signature.sign();

				//set signature value
				basicSignature.setSignatureValue(signatureValue);
				System.out.println("2.2 - Signature added");

				/*
				 * NOTE: At this moment signature has to be extended with
				 * timestamp and revocation data to conform QUALIFIED profile.
				 */
				boolean requestTimeStamp = false; //false to avoid billing
				if (requestTimeStamp) {
					PdfQualifiedSignature qualifiedSignature = basicSignature.qualifiedSignature();
					byte[] signatureValueDigest = qualifiedSignature.getSignatureValueDigest("SHA-256");
					TimeStamp timeStamp = TimeStampGenerator.requestTimeStamp(TimeStampGenerator.DIGEST_ALGORITHM_SHA256, signatureValueDigest, keyAccessor);
					qualifiedSignature.setSignatureTimestamp(timeStamp.getEncoded());
					boolean useTrustedList = true; //whether to use EU Trusted Status List data
					PdfQualifiedSignatureHelper helper = new PdfQualifiedSignatureHelper(certificate, timeStamp, useTrustedList);
					qualifiedSignature.setCertificateValues(helper.getCertificates());
					qualifiedSignature.setRevocationValues(helper.getOCSPs(), helper.getCRLs());
				}

				//complete signature
				basicSignature.complete();
				System.out.println("2.3 - Signature completed");

			} else {
				System.out.println("2 - Signature not added, smartcard access password not provided");
			}

			//save pdf
			fileOutputStream = new FileOutputStream(pdfToWrite);
			pdf.writeTo(fileOutputStream);
			System.out.println("3 - PDF file written to " + pdfToWrite);

		} catch (Exception exc) {
			exc.printStackTrace();

		} finally {
			//close fileInputStream
			if (fileInputStream != null) fileInputStream.close();

			//close fileOutputStream
			if (fileOutputStream != null) fileOutputStream.close();

			//close pdf
			if (pdf != null) pdf.close();
		}

		//done
		System.out.println("Finished");
		System.exit(0);
	}

	public char[] getCardPIN(String tokenSerial, String tokenLabel, long sessionId, X509Certificate cert, boolean isRetry) {
		System.out.println("getCardPIN: " + tokenLabel + ", cert: " + cert.getSubjectX500Principal().toString());
		if (isRetry) {
			//PIN was incorrect, cancel
			System.err.println("getCardPIN: PIN incorrect, exit!");
			return null;
		}

		System.out.println("using PIN: " + new String(password));
		return password;
	}

	public CertificateEntry selectDocumentSigningCertificate(CertificateEntry[] certList) {
		if (certList.length > 0) {
			return certList[0];
		}

		return null;
	}

	public CertificateEntry selectClientAuthenticationCertificate(CertificateEntry[] certList) {
		if (certList.length > 0) {
			return certList[0];
		}

		return null;
	}

}
