/**
 * <copyright> Copyright (c) 2006 by Know-Center, Graz, Austria </copyright>
 * 
 * This software is the confidential and proprietary information of Know-Center,
 * Graz, Austria. You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Know-Center.
 * 
 * KNOW-CENTER MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. KNOW-CENTER SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 * 
 * $Id: Main.java,v 1.5 2006/10/31 08:06:56 wprinz Exp $
 */
package at.gv.egiz.pdfas.commandline;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import at.gv.egiz.pdfas.PdfAsFactory;
import at.gv.egiz.pdfas.api.PdfAs;
import at.gv.egiz.pdfas.api.commons.Constants;
import at.gv.egiz.pdfas.api.exceptions.ConfigUtilsException;
import at.gv.egiz.pdfas.api.exceptions.PdfAsException;
import at.gv.egiz.pdfas.api.io.DataSink;
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.sign.pos.axis.AbsoluteAxisAlgorithm;
import at.gv.egiz.pdfas.api.sign.pos.page.AbsolutePageAlgorithm;
import at.gv.egiz.pdfas.api.sign.pos.page.NewPageAlgorithm;
import at.gv.egiz.pdfas.api.verify.VerifyParameters;
import at.gv.egiz.pdfas.api.verify.VerifyResult;
import at.gv.egiz.pdfas.api.verify.VerifyResults;
import at.gv.egiz.pdfas.exceptions.ErrorCode;
import at.gv.egiz.pdfas.exceptions.ErrorCodeHelper;
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.gv.egiz.pdfas.io.StringTextBasedDataSource;
import at.gv.egiz.pdfas.utils.ConfigUtils;
import at.knowcenter.wag.egov.egiz.PdfAS;
import at.knowcenter.wag.egov.egiz.PdfASID;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.ConnectorFactoryException;
import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
import at.knowcenter.wag.egov.egiz.exceptions.PresentableException;
import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException;
import at.knowcenter.wag.egov.egiz.exceptions.SignatureTypesException;
import at.knowcenter.wag.egov.egiz.framework.SignatorFactory;
import at.knowcenter.wag.egov.egiz.pdf.TablePos;
import at.knowcenter.wag.egov.egiz.sig.ConnectorFactory;
import at.knowcenter.wag.egov.egiz.sig.ConnectorInformation;
import at.knowcenter.wag.egov.egiz.sig.SignatureTypes;

/**
 * The main program entry point of the commandline tool.
 * 
 * <p>
 * The commandline uses the PDF-AS API.
 * </p>
 * 
 * @author wprinz
 */
public abstract class Main
{
// 23.11.2010 changed by exthex - added parameters for placeholder handling
  /**
   * Command line parameter setting the application mode sign|verify
   */
  protected static final String PARAMETER_MODE = "-mode";

  /**
   * Command line parameter setting the application to connect
   */
  protected static final String PARAMETER_CONNECTOR = "-connector";

  /**
   * Command line parameter setting the signature mode.
   */
  protected static final String PARAMETER_SIGNATURE_MODE = "-sigmode";

  /**
   * Command line parameter setting the signature type.
   */
  protected static final String PARAMETER_SIGNATURE_TYPE = "-sigtype";

  /**
   * Command line parameter setting the username
   */
  protected static final String PARAMETER_USER_NAME = "-username";

  /**
   * Command line parameter setting the users password
   */
  protected static final String PARAMETER_USER_PASSWORD = "-password";

  /**
   * Command line parameter selecting the position of the signature.
   */
  protected static final String PARAMETER_POS = "-pos";

  /**
   * Command line parameter signaling to search the source document for a placeholder for the signature
   */
  protected static final String PARAMETER_PLACEHOLDER_SEARCH = "-searchplaceholder";
  
  /**
   * Command line parameter selecting the id of the placeholder to use
   */
  protected static final String PARAMETER_PLACEHOLDER_ID = "-placeholder_id";
  
  /**
   * Command line parameter selecting the match mode for the placeholder
   */
  protected static final String PARAMETER_PLACEHOLDER_MATCH_MODE = "-placeholder_matchmode";

  /**
   * Command line parameter selecting the signature which is going to be
   * verified.
   */
  protected static final String PARAMETER_VERIFY_WHICH = "-verify_which";
  
  /**
   * Command line parameter that starts the deployment of the default configuration to the current
   * user's home folder.
   * @see Constants#USERHOME_CONFIG_FOLDER
   */
  protected static final String PARAMETER_DEPLOY_DEFAULT_CONFIGURATION = "-ddc";

  /**
   * The application mode sign
   */
  public static final String VALUE_MODE_SIGN = "sign";

  /**
   * The application mode verify
   */
  public static final String VALUE_MODE_VERIFY = "verify";

  /**
   * The value for signature mode binary.
   */
  public static final String VALUE_SIGNATURE_MODE_BINARY = Constants.SIGNATURE_TYPE_BINARY;

  /**
   * The value for signature mode textual.
   */
  public static final String VALUE_SIGNATURE_MODE_TEXTUAL = Constants.SIGNATURE_TYPE_TEXTUAL;

  /**
   * The value for signature mode detached.
   */
  public static final String VALUE_SIGNATURE_MODE_DETACHED = "detached";

  /**
   * The value for signature mode detachedtextual.
   */
  public static final String VALUE_SIGNATURE_MODE_DETACHED_TEXT = Constants.SIGNATURE_TYPE_DETACHEDTEXTUAL;

  /**
   * The placeholder match mode STRICT
   */
  public static final String VALUE_PLACEHOLDER_MATCH_MODE_STRICT = "strict";

  /**
   * The placeholder match mode STRICT
   */
  public static final String VALUE_PLACEHOLDER_MATCH_MODE_MODERATE = "moderate";

  /**
   * The placeholder match mode STRICT
   */
  public static final String VALUE_PLACEHOLDER_MATCH_MODE_LENIENT = "lenient";
  
  /**
   * The log.
   */
  private static final Log logger_ = LogFactory.getLog(Main.class);

  /**
   * Main program entry point.
   * 
   * @param args
   *          The commandline arguments.
   * @throws IOException
   */
  public static void main(String[] args) throws IOException
  {
    // ConfigLogger.setLevel(Level.DEBUG);

     // search for PARAMETER_DEPLOY_DEFAULT_CONFIGURATION before initializing the SettingsReader
     for (int i = 0; i < args.length; i++) {
        if (args[i].trim().equals(PARAMETER_DEPLOY_DEFAULT_CONFIGURATION)) {
           try {
              String defaultConfigurationDeployedTo = ConfigUtils.deployDefaultConfiguration(); 
              if (defaultConfigurationDeployedTo != null) {
                 System.out.println("Default configuration successfully deployed to \"" + defaultConfigurationDeployedTo + "\".");
              } else {
                 System.out.println("Default configuration has NOT been deployed. Maybe the configuration already exists.");
              }
              if (args.length == 1) {
                 // no other parameters supplied; exit application
                 System.exit(0);
              }
           } catch (ConfigUtilsException e) {
              System.err.println("Deployment of default configuration failed: " + e.getMessage());
              System.exit(1);
           } 
        }
     }

    SettingsReader.initializeForCommandLine();
    ConfigUtils.initializeLogger();

    // printUsage(System.out);

    String mode = null;
    String signature_mode = Constants.DEFAULT_SIGNATURE_TYPE;
    String connector = null;

    String signature_type = null;
    String user_name = null;
    String user_password = null;
    String pos_string = null;

    boolean search_placeholder = true;
    String placeholderId = null;
    int placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_MODERATE;

    int verify_which = -1;

    String input = null;
    String output = null;

    try
    {
       // force settings preload
       SettingsReader.getInstance();

      // for (int i = 0; i < args.length; i++)
      // {
      // logger_.debug("arg[" + i + "] = " + args[i]);
      // }

      for (int i = 0; i < args.length; i++)
      {
        String cur_arg = args[i].trim();

        if (cur_arg.equals(PARAMETER_DEPLOY_DEFAULT_CONFIGURATION)) {
           // already applied
           continue;
        }
        
        if (cur_arg.equals(PARAMETER_MODE))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_MODE);
            return;
          }
          mode = args[i];
          if (!checkMode(mode))
          {
            printUnrecognizedValue(PARAMETER_MODE, mode);
            return;
          }
          continue;
        }

        if (cur_arg.equals(PARAMETER_CONNECTOR))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_CONNECTOR);
            return;
          }
          connector = args[i];
          if (!checkConnector(connector))
          {
            printUnrecognizedValue(PARAMETER_CONNECTOR, connector);
            return;
          }
          continue;
        }

        if (cur_arg.equals(PARAMETER_SIGNATURE_MODE))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_SIGNATURE_MODE);
            return;
          }
          signature_mode = args[i];
          if (!checkSignatureMode(signature_mode))
          {
            printUnrecognizedValue(PARAMETER_SIGNATURE_MODE, signature_mode);
            return;
          }
          continue;
        }

        if (cur_arg.equals(PARAMETER_SIGNATURE_TYPE))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_SIGNATURE_TYPE);
            return;
          }
          signature_type = args[i];
          if (!checkSignatureType(signature_type))
          {
            printUnrecognizedValue(PARAMETER_SIGNATURE_TYPE, signature_type);
            return;
          }
          continue;
        }

        if (cur_arg.equals(PARAMETER_PLACEHOLDER_SEARCH))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_PLACEHOLDER_SEARCH);
            return;
          }
          search_placeholder = Boolean.valueOf(args[i]).booleanValue();
          continue;
        }

        if (cur_arg.equals(PARAMETER_PLACEHOLDER_ID))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_PLACEHOLDER_ID);
            return;
          }
          placeholderId = args[i];
          continue;
        }

        if (cur_arg.equals(PARAMETER_PLACEHOLDER_MATCH_MODE))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_PLACEHOLDER_MATCH_MODE);
            return;
          }
          String matchMode = args[i];
          if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_LENIENT))
             placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_LENIENT;
          else if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_MODERATE))
             placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_MODERATE;
          else if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_STRICT))
             placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_STRICT;
          else
             printUnrecognizedValue(PARAMETER_PLACEHOLDER_MATCH_MODE, args[i]);
          continue;
        }

        if (cur_arg.equals(PARAMETER_USER_NAME))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_USER_NAME);
            return;
          }
          user_name = args[i];
          continue;
        }

        if (cur_arg.equals(PARAMETER_USER_PASSWORD))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_USER_PASSWORD);
            return;
          }
          user_password = args[i];
          continue;
        }

        if (cur_arg.equals(PARAMETER_POS))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_POS);
            return;
          }
          pos_string = args[i];
          continue;
        }

        if (cur_arg.equals(PARAMETER_VERIFY_WHICH))
        {
          i++;
          if (i >= args.length)
          {
            printNoValue(PARAMETER_VERIFY_WHICH);
            return;
          }
          String str_verify_which = args[i];
          try
          {
            verify_which = Integer.parseInt(str_verify_which);
          }
          catch (NumberFormatException e)
          {
            printUnrecognizedValue(PARAMETER_VERIFY_WHICH, str_verify_which);
            return;
          }

          continue;
        }

        if (cur_arg.charAt(0) == '-')
        {
          printUnrecognizedOption(cur_arg);
          return;
        }

        if (input == null)
        {
          input = cur_arg;
          continue;
        }

        if (output == null)
        {
          output = cur_arg;
          continue;
        }

        printUnrecognizedAdditionalCommandlineArgument(cur_arg);
        return;
      }

      if (mode == null)
      {
        printMissingParameter("a mode", PARAMETER_MODE);
        return;
      }
      if (connector == null)
      {
        printMissingParameter("a connector", PARAMETER_CONNECTOR);
        return;
      }
      if (mode.equals(VALUE_MODE_SIGN))
      {
        if (signature_mode == null)
        {
          printMissingParameter("a signature mode", PARAMETER_SIGNATURE_MODE);
          return;
        }
        if (signature_type == null)
        {
          SettingsReader settings = SettingsReader.getInstance();
          String default_type = settings.getValueFromKey(SignatureTypes.DEFAULT_TYPE);
          signature_type = default_type;
        }
        if (user_name == null)
        {
          user_name = "";
          // printMissingParameter("a user name", PARAMETER_USER_NAME);
          // return;
        }
        if (user_password == null)
        {
          user_password = "";
          // printMissingParameter("a user password", PARAMETER_USER_PASSWORD);
          // return;
        }
      }

      if (input == null)
      {
        printMissing("an input document");
        return;
      }

      File file = new File(input);
      if (!file.exists())
      {
        System.err.println("The input file '" + input + "' doesn't exist.");
        return;
      }

      if (mode.equals(VALUE_MODE_SIGN) && output == null)
      {
        output = generateOutputFileNameFromInput(input, signature_mode);
      }

      carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string, search_placeholder, placeholderId, placeholderMatchMode);

    }
    catch (PdfAsException e)
    {
      printPresentableException(e);

      if (output != null)
      {
        File oFile = new File(output);
        if (oFile.exists()) {
           logger_.debug("Deleting output file on error.");
           boolean deleted = oFile.delete();
           if (!deleted)
           {
             logger_.error("Couldn't delete output file " + output);
           }
        }
      }
   }
    finally
    {
      SettingsReader.clearTemporaryDirectory();
    }
  }

  protected static void carryOutCommand(final String mode, final String signature_mode, final String connector, final String signature_type, final String user_name, final String user_password,
      final int verify_which, final String input, String output, final String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException
  {
    // File file = new File(input);
    //
    // byte[] input_bytes = null;
    // try
    // {
    // FileInputStream fis = new FileInputStream(file);
    // input_bytes = new byte[(int) file.length()];
    // fis.read(input_bytes);
    // fis.close();
    // }
    // catch (IOException e)
    // {
    // throw new PDFDocumentException(201);
    // }

    PrintWriter messageOutput = new PrintWriter(System.out);

    if (mode.equals(VALUE_MODE_SIGN))
    {
      carryOutSign(input, connector, signature_mode, signature_type, pos_string, user_name, user_password, output, messageOutput, search_placeholder, placeholderId, placeholderMatchMode);
    }
    else
    {
      carryOutVerify(input, connector, verify_which, messageOutput);
    }
    messageOutput.flush();
  }

  public static void carryOutSign(String input, String connector, String signature_mode, String signature_type, String pos_string, String user_name, String user_password, String output,
      PrintWriter messageOutput, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException
  {
    messageOutput.println("Signing " + input + "...");

    // for performance measurement
    long startTime = 0;
    long fileSize = 0;
    if (logger_.isInfoEnabled())
    {
      startTime = System.currentTimeMillis();
    }

    DataSource dataSource = null;
    try
    {
      File file = new File(input);
      dataSource = new FileBasedDataSource(file, "application/pdf");
      if (logger_.isDebugEnabled())
      {
        fileSize = file.length();
      }
    }
    catch (IOException e)
    {
      throw new PDFDocumentException(201, e);
    }

    DataSink dataSink = null;
    File outputFile = null;
    try
    {
      outputFile = new File(output);

      dataSink = new FileBasedDataSink(outputFile);
    }
    catch (IOException e)
    {
      throw new PDFDocumentException(ErrorCode.CANNOT_WRITE_PDF, e);
    }

    try {
       processSign(dataSource, connector, signature_mode, signature_type, pos_string, search_placeholder, placeholderId, placeholderMatchMode, dataSink);
    } catch (Exception e) {
       // Exception caught in order to delete file based datasink
       if (outputFile != null && outputFile.exists())
       {
         logger_.debug("Deleting output file on error.");
         boolean deleted = outputFile.delete();
         if (!deleted)
         {
           logger_.error("Couldn't delete output file " + output);
         }
       }
       if (e instanceof PresentableException) {
          throw (PresentableException)e;
       } else {
    	  logger_.error(e);
          throw new PresentableException(ErrorCode.UNKNOWN_ERROR, e);
       }
    }

    // for performance measurement
    if (logger_.isInfoEnabled())
    {
      long endTime = System.currentTimeMillis();
      String toReport = "SIGN;" + signature_mode + ";" + input + ";" + fileSize + ";" + (endTime - startTime);
      logger_.info(toReport);
    }

    messageOutput.println("Signing was successful (" + output + ").");
  }

  public static void carryOutVerify(String input, String connector, int verify_which, PrintWriter messageOutput) throws PdfAsException
  {
    messageOutput.println("Verifying " + input + "...");

    // for performance measurement
    long startTime = 0;
    long fileSize = 0;
    if (logger_.isInfoEnabled())
    {
      startTime = System.currentTimeMillis();
    }

    DataSource dataSource = null;
    try
    {
      File file = new File(input);
      if (logger_.isDebugEnabled())
      {
        fileSize = file.length();
      }
      String extension = extractExtension(input);
      if (extension != null && extension.equals("txt"))
      {
        try
        {
          FileInputStream fis = new FileInputStream(file);
          byte[] input_bytes = new byte[(int) file.length()];
          fis.read(input_bytes);
          fis.close();

          String text = new String(input_bytes, "UTF-8");
          dataSource = new StringTextBasedDataSource(text);
        }
        catch (UnsupportedEncodingException e)
        {
          throw new RuntimeException("Very strange: UTF-8 character encoding not supported.", e);
        }
      }
      else
      {
        dataSource = new FileBasedDataSource(file, "application/pdf");
      }
    }
    catch (IOException e)
    {
      throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e);
    }

    VerifyResults results = processVerify(dataSource, connector, verify_which);

    messageOutput.println("Verification results:");
    formatVerifyResults(results, messageOutput);

    // for performance measurement
    if (logger_.isInfoEnabled())
    {
      long endTime = System.currentTimeMillis();
      String toReport = "VERIFY;" + input + ";" + fileSize + ";" + (endTime - startTime) + ";" + debugVerifyResults(results);
      logger_.info(toReport);
    }

  }
  
  /**
   * Extracts the extension from a file name string.
   * 
   * <p>
   * The extension of a file name is whatever text follows the last '.'.
   * </p>
   * 
   * @param file_name
   *          The file name.
   * @return Returns the extension. If the file name ends with the '.', then an
   *         empty string is returned. If the file name doesn't contain any '.'
   *         or file_name is null, null is returned.
   */
  public static String extractExtension(String file_name)
  {
    if (file_name == null)
    {
      return null;
    }

    int dot_index = file_name.lastIndexOf('.');
    if (dot_index < 0)
    {
      return null;
    }
    return file_name.substring(dot_index + 1);
  }

  public static void processSign(DataSource dataSource, String connector, String signature_mode, String signature_type, String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode, DataSink dataSink) throws PdfAsException
  {
    TablePos pos = null;
    if (pos_string != null)
    {
      try
      {
        pos = PdfAS.parsePositionFromPosString(pos_string);
      }
      catch (PDFDocumentException e)
      {
        printUnrecognizedValue(PARAMETER_POS, pos_string);
        return;

      }
    }
    SignaturePositioning posi = null;
    if (pos != null)
    {
      posi = new SignaturePositioning();
      if (!pos.isXauto())
      {
        posi.setXAlgorithm(new AbsoluteAxisAlgorithm(pos.getPosX()));
      }
      if (!pos.isYauto())
      {
        posi.setYAlgorithm(new AbsoluteAxisAlgorithm(pos.getPosY()));
      }
      if (!pos.isWauto())
      {
        posi.setWidthAlgorithm(new AbsoluteAxisAlgorithm(pos.getWidth()));
      }
      if (pos.isNewPage())
      {
        posi.setPageAlgorithm(new NewPageAlgorithm());
      }
      if (!pos.isPauto())
      {
        posi.setPageAlgorithm(new AbsolutePageAlgorithm(pos.getPage()));
      }
      posi.setFooterLine(pos.getFooterLine());
    }

    PdfAs pdfAs = PdfAsFactory.createPdfAs(new File(SettingsReader.RESOURCES_PATH));

    SignParameters sp = new SignParameters();
    sp.setDocument(dataSource);
    sp.setOutput(dataSink);
    sp.setSignatureType(signature_mode); // TODO detached signaturen!
    sp.setSignatureDevice(connector);
    sp.setSignatureProfileId(signature_type);
    sp.setSignaturePositioning(posi);
    sp.setCheckForPlaceholder(search_placeholder);
    sp.setPlaceholderId(placeholderId);
    sp.setPlaceholderMatchMode(placeholderMatchMode);
    pdfAs.sign(sp);

  }

  public static VerifyResults processVerify(DataSource dataSource, String connector, int verify_which) throws PdfAsException
  {
    String verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE;
    VerificationFilterParameters parameters = SettingsHelper.readVerificationFilterParametersFromSettings();
    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;
      }
    }

    PdfAs pdfAs = PdfAsFactory.createPdfAs(new File(SettingsReader.RESOURCES_PATH));

    VerifyParameters vp = new VerifyParameters();
    vp.setDocument(dataSource);
    vp.setVerifyMode(verifyMode);
    vp.setSignatureDevice(connector);
    vp.setReturnHashInputData(false);
    vp.setVerificationTime(null);
    vp.setSignatureToVerify(verify_which);
    VerifyResults vrs = pdfAs.verify(vp);

    return vrs;
  }

  protected static String generateOutputFileNameFromInput(String input, String sig_mode)
  {
    String output = input + "_out";
    if (sig_mode.startsWith("detached"))
    {
      output += ".xml";
    }
    else
    {
      output += ".pdf";
    }

    return output;
  }

  /**
   * Prints that the provided option was unrecognized.
   * 
   * @param option
   *          The unrecognized option.
   * @throws PresentableException
   *           Forwarded exception.
   */
  protected static void printUnrecognizedOption(final String option) throws PresentableException
  {
    System.err.println("Unrecognized option '" + option + "'.");
    printUsage(System.out);
  }

  /**
   * Prints that the provided value was unrecognized.
   * 
   * @param parameter
   *          The parameter, which is missing a value.
   * @throws PresentableException
   *           Forwarded exception.
   */
  protected static void printNoValue(final String parameter) throws PresentableException
  {
    System.err.println("The parameter " + parameter + " requires a value as next argument.");
    printUsage(System.out);
  }

  /**
   * Prints that the provided value was unrecognized.
   * 
   * @param value
   *          The unrecognized value.
   * @throws PresentableException
   *           Forwarded exception.
   */
  protected static void printUnrecognizedValue(final String parameter, final String value) throws PresentableException
  {
    System.err.println("The parameter " + parameter + " doesn't recognize the provided value '" + value + "'.");
    printUsage(System.out);
  }

  /**
   * Prints that the provided additional commandline argument was unrecognized.
   * 
   * @param argument
   *          The unrecognized argument.
   * @throws PresentableException
   *           Forwarded exception.
   */
  protected static void printUnrecognizedAdditionalCommandlineArgument(final String argument) throws PresentableException
  {
    System.err.println("Unrecognized additional commandline argument '" + argument + "'.");
    printUsage(System.out);
  }

  /**
   * Prints that a certain parameter was missing.
   * 
   * @param missing_term
   *          A description of the missing parameter ("e.g. a mode").
   * @param parameter
   *          The missing parameter itself (e.g. "-mode").
   * @throws PresentableException
   *           Forwarded exception.
   */
  protected static void printMissingParameter(final String missing_term, final String parameter) throws PresentableException
  {
    printMissing(missing_term + " ('" + parameter + "' parameter)");
  }

  /**
   * Prints that something is missing.
   * 
   * @param missing_term
   *          A descriptive message of the missing thing.
   * @throws PresentableException
   *           Forwarded exception.
   */
  protected static void printMissing(final String missing_term) throws PresentableException
  {
    System.err.println("Please specify " + missing_term + ".");
    printUsage(System.out);
  }

  /**
   * Prints out the ErrorCodeException in a descriptive form.
   * 
   * @param ece
   *          The ErrorCodeException to be printed.
   */
  protected static void printPresentableException(final PdfAsException e)
  {
    String errorMessage = ErrorCodeHelper.formErrorMessage(e);
    System.err.println(errorMessage);
//    if (e.getErrorCode() == ErrorCode.PLACEHOLDER_EXCEPTION)
//    {
//      PlaceholderException phe = null;
//      if (e instanceof PlaceholderException)
//      {
//        phe = (PlaceholderException) e;
//      }
//      else
//      {
//        phe = (PlaceholderException) e.getCause();
//      }
//
//      System.err.println("Der Platzhalter des Feldes " + phe.getField() + " ist um " + phe.getMissing() + " Bytes zu kurz. ");
//    }
//
//    System.err.println("Fehler " + e.getErrorCode() + ": " + ErrorCodeHelper.getMessageForErrorCode(e.getErrorCode()));
//
//    if (e instanceof ExternalErrorException)
//    {
//      ExternalErrorException eee = (ExternalErrorException) e;
//      System.err.println("Externer Fehlergrund: " + eee.getExternalErrorCode() + ": " + eee.getExternalErrorMessage());
//    }

    logger_.error(e);
  }

  /**
   * Prints the usage text.
   * 
   * @param writer
   *          The writer to print the text to.
   * @throws PresentableException
   *           Forwarded exception.
   */
  public static void printUsage(PrintStream writer) throws PresentableException
  {
    writer.println("Usage: pdf-as [[OPTIONS] <input file> [output file]|UTILOPTIONS]");
    writer.println("  Required OPTIONS:");

    writer.println("    " + PARAMETER_MODE + " <" + VALUE_MODE_SIGN + "|" + VALUE_MODE_VERIFY + ">");
    writer.println("      " + VALUE_MODE_SIGN + " ... signs a document");
    writer.println("      " + VALUE_MODE_VERIFY + " ... verifies a document");

    writer.print("    " + PARAMETER_CONNECTOR + " ");
    ConnectorInformation[] ci = ConnectorFactory.getConnectorInformationArray();
    
    // prepare list of connectors available for commandline
    ArrayList ciList = new ArrayList();
    for (int i = 0; i < ci.length; i++) {
       String id = ci[i].getIdentifier();
       if (ConnectorFactory.isAvailableForCommandline(id)) {
          ciList.add(ci[i]);
       }
    }
    
    // list available connectors wrapped in <...|...>
    Iterator ciIt = ciList.iterator();
    writer.print("<");
    while (ciIt.hasNext()) {
       writer.print(((ConnectorInformation) ciIt.next()).getIdentifier());
       if (ciIt.hasNext()) {
          writer.print("|");
       }
    }
    writer.print(">");
    writer.println();
    
//    for (int i = 0; i < ci.length; i++)
//    {
//      String id = ci[i].getIdentifier();
//      if (!ConnectorFactory.isAvailableForCommandline(id))
//      {
//        continue;
//      }
//      writer.print(id);
//      if (i < ci.length - 1)
//      {
//        writer.print("|");
//      }
//    }
    ciIt = ciList.iterator();
    while (ciIt.hasNext()) {
       ConnectorInformation cinf = (ConnectorInformation) ciIt.next();
       writer.println("      " + cinf.getIdentifier() + " ... " + cinf.getDescription());
    }

//    for (int i = 0; i < ci.length; i++)
//    {
//      String id = ci[i].getIdentifier();
//      if (!ConnectorFactory.isAvailableForCommandline(id))
//      {
//        continue;
//      }
//      writer.println("      " + id + " ... " + ci[i].getDescription());
//    }

    writer.println("  UTILity OPTIONS");

    writer.println("    " + PARAMETER_DEPLOY_DEFAULT_CONFIGURATION + " ... deploys the default configuration to the current user's home");

    writer.println("  OPTIONS for signation:");

    writer.println("    " + PARAMETER_SIGNATURE_MODE + " <" + VALUE_SIGNATURE_MODE_BINARY + "|" + VALUE_SIGNATURE_MODE_TEXTUAL + "> [optional]");
    writer.println("      " + VALUE_SIGNATURE_MODE_BINARY + " ... signs the complete binary document" + (Constants.DEFAULT_SIGNATURE_TYPE.equals(VALUE_SIGNATURE_MODE_BINARY) ? " (default)" : ""));
    writer.println("      " + VALUE_SIGNATURE_MODE_TEXTUAL + " ... signs only the textual portion of the document" + (Constants.DEFAULT_SIGNATURE_TYPE.equals(VALUE_SIGNATURE_MODE_TEXTUAL) ? " (default)" : ""));
    writer.println("      " + VALUE_SIGNATURE_MODE_DETACHED_TEXT + " ... signs the document using the textual mode and returns the xml signature of it." + (Constants.DEFAULT_SIGNATURE_TYPE.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXT) ? " (default)" : ""));

    writer.print("    " + PARAMETER_SIGNATURE_TYPE + " <");
    SignatureTypes sig_types = SignatureTypes.getInstance();
    SettingsReader settings = SettingsReader.getInstance();
    Set types_array = sig_types.getSignatureTypes();
    Iterator it = types_array.iterator();
    while (it.hasNext())
    {
      String type = (String) it.next();
      writer.print(type);
      if (it.hasNext())
      {
        writer.print("|");
      }
    }
    writer.println(">");
    writer.println("      ... [optional] the profile to be used. If omitted, the default");
    writer.println("          profile is used.");
    String default_type = settings.getValueFromKey(SignatureTypes.DEFAULT_TYPE);
    it = types_array.iterator();
    while (it.hasNext())
    {
      String type = (String) it.next();
      String descr_key = SignatureTypes.SIG_OBJ + type + "." + SignatureTypes.SIG_DESCR;
      String type_descr = settings.getValueFromKey(descr_key);

      writer.println("      " + type + " ... " + (type.equals(default_type) ? "(default) " : "") + type_descr);
    }

    writer.println("    " + PARAMETER_USER_NAME + " <user_name> ... [optional] the user name");
    writer.println("    " + PARAMETER_USER_PASSWORD + " <password> ... [optional] the user password");

    writer.println("    " + PARAMETER_POS + " <position> ... [optional] the position of the signature block");
    writer.println("      position has the format [x:x_algo];[y:y_algo];[w:w_algo][p:p_algo];[f:f_algo]");
    writer.println("      if not present default is set to  x:auto;y:auto;w:auto;p:auto;f:0");
    writer.println("      x_algo:='auto'     ... automatic positioning x");
    writer.println("              floatvalue ... absolute x must be >= 0");
    writer.println("      y_algo:='auto'     ... automatic positioning y");
    writer.println("              floatvalue ... absolute y must be >= 0");
    writer.println("      w_algo:='auto'     ... automatic width");
    writer.println("              floatvalue ... absolute width must be > 0");
    writer.println("      p_algo:='auto'     ... automatic last page");
    writer.println("              'new'      ... new page");
    writer.println("              intvalue   ... pagenumber must be > 0 if p>number of pages in document p-->handled like p:'new'");
    writer.println("      f_algo  floatvalue ... consider footerline must be >= 0 (only if y_algo is auto and p_algo is not 'new')");

    writer.println("    " + PARAMETER_PLACEHOLDER_SEARCH + " <true|false> ... [optional] if set to true, the source document will be scanned for signature placeholder images. If not set, the enable_placeholder_search value in the config file decides whether or not a search for placeholder images will be performed.");
    writer.println("    " + PARAMETER_PLACEHOLDER_ID + " <id> ... [optional] search for signature placeholder images containing the given id");
    writer.println("    " + PARAMETER_PLACEHOLDER_MATCH_MODE + " <" + VALUE_PLACEHOLDER_MATCH_MODE_LENIENT + "|" + VALUE_PLACEHOLDER_MATCH_MODE_MODERATE + "|" + VALUE_PLACEHOLDER_MATCH_MODE_STRICT + "> ... [optional] specify the behavior if no matching placeholder could be found. Default is <moderate>.");
    writer.println("      " + VALUE_PLACEHOLDER_MATCH_MODE_LENIENT + " ... sign in place of the first found placeholder, regardless if it matches exactly, or at the end of the document if none is found.");
    writer.println("      " + VALUE_PLACEHOLDER_MATCH_MODE_MODERATE + " ... sign in place of the first found placeholder which has no explicit id set, or at the end of the document if none is found.");
    writer.println("      " + VALUE_PLACEHOLDER_MATCH_MODE_STRICT + " ... throws a PlaceholderExtractionException.");

    writer.println("  OPTIONS for verification:");
    writer.println("    " + PARAMETER_VERIFY_WHICH + " <number> ... [optional] zero based number of the signature");
    writer.println("      to be verified. If omitted, all signatures are verified.");

    writer.println("  Example usage:");
    writer.println("    pdf-as " + PARAMETER_MODE + " " + VALUE_MODE_SIGN + " " + PARAMETER_CONNECTOR + " " + Constants.SIGNATURE_DEVICE_BKU + " " + PARAMETER_SIGNATURE_MODE + " " + VALUE_SIGNATURE_MODE_TEXTUAL + " some_document.pdf");
    writer.println("    pdf-as " + PARAMETER_MODE + " " + VALUE_MODE_VERIFY + " " + PARAMETER_CONNECTOR + " " + Constants.SIGNATURE_DEVICE_BKU + " some_document.pdf_out.pdf");
    writer.println("    pdf-as " + PARAMETER_DEPLOY_DEFAULT_CONFIGURATION);
  }

  /**
   * Checks the value for correctness.
   * 
   * @param mode
   *          The parameter's value.
   * @return Returns true, if the value is correct, false otherwise.
   */
  protected static boolean checkMode(String mode)
  {
    return mode.equals(VALUE_MODE_SIGN) || mode.equals(VALUE_MODE_VERIFY);
  }

  /**
   * Checks the value for correctness.
   * 
   * @param signature_mode
   *          The parameter's value.
   * @return Returns true, if the value is correct, false otherwise.
   */
  protected static boolean checkSignatureMode(String signature_mode)
  {
    return signature_mode.equals(VALUE_SIGNATURE_MODE_BINARY) || signature_mode.equals(VALUE_SIGNATURE_MODE_TEXTUAL)
    //|| signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED)
        || signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXT);
  }

  /**
   * Checks the value for correctness.
   * 
   * @param connector
   *          The parameter's value.
   * @return Returns true, if the value is correct, false otherwise.
   * @throws ConnectorFactoryException
   *           F.e.
   */
  protected static boolean checkConnector(String connector) throws ConnectorFactoryException
  {
    return ConnectorFactory.isValidConnectorIdentifier(connector) && ConnectorFactory.isAvailableForCommandline(connector);
  }

  /**
   * Checks the value for correctness (meaning if it exists)
   * 
   * @param signature_type
   *          The parameter's value.
   * @return Returns true, if the value is correct, false otherwise.
   */
  protected static boolean checkSignatureType(String signature_type) throws SignatureTypesException
  {
    return SignatureTypes.getInstance().getSignatureTypes().contains(signature_type);
  }
    
  /**
   * Translates the commandline argument to a PDF-AS-ID.
   * 
   * @param signature_mode
   *          The signator mode commandline argument.
   * @return Returns the corresponding PDFASID.
   */
  protected static PdfASID translateSignatureModeToPdfASID(String signature_mode)
  {
    if (signature_mode.equals(VALUE_SIGNATURE_MODE_BINARY))
    {
      return SignatorFactory.MOST_RECENT_BINARY_SIGNATOR_ID;
    }
    if (signature_mode.equals(VALUE_SIGNATURE_MODE_TEXTUAL))
    {
      return SignatorFactory.MOST_RECENT_TEXTUAL_SIGNATOR_ID;
    }
    if (signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED))
    {
      return SignatorFactory.MOST_RECENT_DETACHED_SIGNATOR_ID;
    }
    if (signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXT))
    {
      return SignatorFactory.MOST_RECENT_DETACHEDTEXT_SIGNATOR_ID;
    }
    return null;
  }

  /**
   * Formats the verification results.
   * 
   * @param results
   *          The List of SignatureResponse verification results.
   * @param writer
   *          The output sink to write the formatted text to.
   * @throws SettingNotFoundException
   *           Forwarded exception.
   */
  protected static void formatVerifyResults(VerifyResults results, PrintWriter writer) throws SettingNotFoundException
  {
    Iterator it = results.getResults().iterator();
    while (it.hasNext())
    {
      VerifyResult result = (VerifyResult) it.next();
      formatVerifyResult(result, writer);

      if (it.hasNext())
      {
        writer.println();
      }
    }
  }

  /**
   * Formats the verification results for debugging. Returns 0 if no error
   * occurs or the sum of all error-codes.
   * 
   * @param results
   * 
   * @param writer
   *          The output sink to write the formatted text to.
   * @throws SettingNotFoundException
   *           Forwarded exception.
   */
  protected static int debugVerifyResults(VerifyResults results) throws SettingNotFoundException
  {
    int toreturn = 0;
    Iterator it = results.getResults().iterator();
    while (it.hasNext())
    {
      VerifyResult result = (VerifyResult) it.next();

      toreturn += result.getValueCheckCode().getCode();
    }
    return toreturn;
  }

  public static void formatVerifyResult(VerifyResult result, PrintWriter writer) throws SettingNotFoundException
  {

     if (result.isVerificationDone()) {
        writer.println("  Zertifikat:");
        writer.println("    Signator:     " + result.getSignerCertificate().getSubjectDN().toString());
        writer.println("    Aussteller:   " + result.getSignerCertificate().getIssuerDN().toString());
        writer.println("    Seriennummer: " + result.getSignerCertificate().getSerialNumber());
        List public_properties = result.getPublicProperties();
        Iterator it = public_properties.iterator();
        while (it.hasNext())
        {
           String public_property = (String) it.next();
           writer.println("    Eigenschaft:  " + public_property);
        }
        
        writer.println("  Zertifikat-Check:");
        writer.println("    " + result.getCertificateCheck().getCode() + " - " + result.getCertificateCheck().getMessage());
        writer.println("  Signatur-Check:");
        writer.println("    " + result.getValueCheckCode().getCode() + " - " + result.getValueCheckCode().getMessage());
        writer.println("  Manifest-Check:");
        writer.println("    " + result.getManifestCheckCode().getCode() + " - " + result.getManifestCheckCode().getMessage());
     } else {
        PdfAsException ex = result.getVerificationException();
        writer.println("\n  Signaturpr�fung f�r diese Signatur nicht m�glich: " + ErrorCodeHelper.formErrorMessage(ex));
     }
  }

}