package demo;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;

import at.gv.egiz.pdfas.PdfAsFactory;
import at.gv.egiz.pdfas.api.PdfAs;
import at.gv.egiz.pdfas.api.analyze.AnalyzeParameters;
import at.gv.egiz.pdfas.api.analyze.AnalyzeResult;
import at.gv.egiz.pdfas.api.commons.Constants;
import at.gv.egiz.pdfas.api.commons.SignatureProfile;
import at.gv.egiz.pdfas.api.exceptions.PdfAsException;
import at.gv.egiz.pdfas.api.io.DataSource;
import at.gv.egiz.pdfas.api.sign.SignParameters;
import at.gv.egiz.pdfas.api.sign.pos.SignaturePositioning;
import at.gv.egiz.pdfas.api.verify.VerifyAfterAnalysisParameters;
import at.gv.egiz.pdfas.api.verify.VerifyResult;
import at.gv.egiz.pdfas.commandline.Main;
import at.gv.egiz.pdfas.framework.config.SettingsHelper;
import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters;
import at.gv.egiz.pdfas.io.FileBasedDataSink;
import at.gv.egiz.pdfas.io.FileBasedDataSource;
import at.knowcenter.wag.egov.egiz.sig.SignatureTypeDefinition;
import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;

/**
 * Performs signatures and verifications with the pdfas api.
 * @author tknall
 */
public final class APIDemo {

   /**
    * Disable instantiation.
    */
   private APIDemo() {
   }

   /**
    * Verifies a pdf document.
    * 
    * @param pdfasAPI
    *           The instantiated api.
    * @param source
    *           The signed pdf document.
    * @param verifyDevice
    *           The device used for verification (see
    *           {@link Constants#SIGNATURE_DEVICE_MOA} and
    *           {@link Constants#SIGNATURE_DEVICE_BKU}).
    * @throws PdfAsException
    *            Thrown in case of an error.
    * @throws IOException
    *            Thrown in case of an i/o error.
    */
   public static void verify(PdfAs pdfasAPI, File source, String verifyDevice) throws PdfAsException, IOException {

      // set source
      DataSource dataSource = new FileBasedDataSource(source, "application/pdf");

      // evaluate settings
      VerificationFilterParameters parameters = SettingsHelper.readVerificationFilterParametersFromSettings();
      String verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE;

      if (parameters.extractBinarySignaturesOnly()) {
         verifyMode = Constants.VERIFY_MODE_BINARY_ONLY;
      } else if (parameters.assumeOnlySignatureUpdateBlocks()) {
         verifyMode = Constants.VERIFY_MODE_SEMI_CONSERVATIVE;
      } else {
         verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE;
      }

      // configure analyze parameters
      AnalyzeParameters analyzeParameters = new AnalyzeParameters();
      analyzeParameters.setDocument(dataSource);
      analyzeParameters.setVerifyMode(verifyMode);

      // analyze
      System.out.println("Analyzing...");
      AnalyzeResult analyzeResult = pdfasAPI.analyze(analyzeParameters);
      System.out.println("Successfully analyzed.");

      // setup verification
      VerifyAfterAnalysisParameters vaap = new VerifyAfterAnalysisParameters();
      vaap.setAnalyzeResult(analyzeResult);
      vaap.setReturnHashInputData(true);
      vaap.setSignatureDevice(verifyDevice);
      vaap.setVerificationTime(null);

      // invoke verification
      System.out.println("Verifying...");
      List verifyResults = pdfasAPI.verify(vaap).getResults();
      System.out.println("Verification complete.");

      // iterate over results
      PrintWriter out = new PrintWriter(System.out);
      Iterator it = verifyResults.iterator();
      while (it.hasNext()) {
         VerifyResult result = (VerifyResult) it.next();
         Main.formatVerifyResult(result, out);
      }
      out.flush();

   }

   /**
    * Signs a pdf document.
    * 
    * @param pdfasAPI
    *           The instantiated api.
    * @param source
    *           The unsigned pdf document.
    * @param signatureDevice
    *           The device used for signature (see
    *           {@link Constants#SIGNATURE_DEVICE_MOA} and
    *           {@link Constants#SIGNATURE_DEVICE_BKU}).
    * @throws PdfAsException
    *            Thrown in case of an error.
    * @throws IOException
    *            Thrown in case of an i/o error.
    * @param destination
    *           The signed pdf document.
    * @param signatureMode
    *           The mode used for signature (see
    *           {@link Constants#SIGNATURE_TYPE_BINARY} resp.
    *           {@link Constants#SIGNATURE_TYPE_TEXTUAL} or
    *           {@link Constants#SIGNATURE_TYPE_DETACHEDTEXTUAL}).
    * @param signatureProfile
    *           The profile used for signature.
    * @param signaturePos
    *           The position of the signature (see {@link SignaturePositioning})
    *           or {@code null}.
    */
   private static void sign(PdfAs pdfasAPI, File source, File destination, String signatureMode, String signatureDevice, String signatureProfile, SignaturePositioning signaturePos)
         throws PdfAsException, IOException {

      // set source
      DataSource dataSource = new FileBasedDataSource(source, "application/pdf");

      // set output
      FileBasedDataSink dataSink = new FileBasedDataSink(destination);

      // configure signature
      SignParameters signParameters = new SignParameters();
      signParameters.setDocument(dataSource);
      signParameters.setOutput(dataSink);
      signParameters.setSignatureType(signatureMode);
      signParameters.setSignatureDevice(signatureDevice);
      signParameters.setSignatureProfileId(signatureProfile);
      signParameters.setSignaturePositioning(signaturePos);
      // override key identifier from profile
//      signParameters.setSignatureKeyIdentifier("KG_allgemein");

      // sign
      System.out.println("Signing...");
      pdfasAPI.sign(signParameters);

      System.out.println("Successfully signed.");

   }

   /**
    * Signs a test document with each available profile. Note that minimal layout profiles lead
    * to an error textual signing the document.
    */
   public static void signWithAllProfilesDemo() {
      File configdir = new File("./work");
      File unsignedFile = new File("./test-files/blindtext.pdf");

      String signatureMode = Constants.SIGNATURE_TYPE_TEXTUAL;
      String signatureDevice = Constants.SIGNATURE_DEVICE_MOA;
      SignaturePositioning signaturePos = null;

      try {
         
         // instantiate api
         PdfAs pdfasAPI = PdfAsFactory.createPdfAs(configdir);
         
         // warning: This method is not specified via api. It is only used to determine textual
         // capable profiles
         SignatureTypes sig_types = SignatureTypes.getInstance();

         // ok, defined via api
         Iterator profiles = pdfasAPI.getProfileInformation().iterator();
         while (profiles.hasNext()) {
            SignatureProfile profile = (SignatureProfile) profiles.next();
   
            SignatureTypeDefinition std = sig_types.getSignatureTypeDefinition(profile.getProfileId());
            String currentSignatureMode;
            if (std.isTextExtractable()) {
               currentSignatureMode = signatureMode;
            } else {
               currentSignatureMode = Constants.SIGNATURE_TYPE_BINARY;
            }

            File signedFile = new File("./test-files/blindtext_" + currentSignatureMode + "-signed_" + profile.getProfileId() + ".pdf");
            
            sign(pdfasAPI, unsignedFile, signedFile, currentSignatureMode, signatureDevice, profile.getProfileId(), signaturePos);
            verify(pdfasAPI, signedFile, signatureDevice);
         }
         
      } catch (PdfAsException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }

   }

   /**
    * Signs a test document with a single profile.
    */
   public static void signWithSingleProfileDemo() {
      File configdir = new File("./work");
      File unsignedFile = new File("./test-files/blindtext.pdf");

      String signatureMode = Constants.SIGNATURE_TYPE_TEXTUAL;
      String signatureDevice = Constants.SIGNATURE_DEVICE_MOA;
      String signatureProfile = "SIGNATURBLOCK_DE";
      SignaturePositioning signaturePos = null;

      File signedFile = new File("./test-files/blindtext_" + signatureMode + "-signed_" + signatureProfile + ".pdf");

      try {
         
         // instantiate api
         PdfAs pdfasAPI = PdfAsFactory.createPdfAs(configdir);

         sign(pdfasAPI, unsignedFile, signedFile, signatureMode, signatureDevice, signatureProfile, signaturePos);
         verify(pdfasAPI, signedFile, signatureDevice);
         
      } catch (PdfAsException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }

   /**
    * Starts a demo that signs a document and performs a verification
    * afterwards.
    * 
    * @param args
    *           The parameter(s).
    */
   public static void main(String[] args) {

//      signWithSingleProfileDemo();
      signWithAllProfilesDemo();

   }

}