/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
/* jshint node:true */
'use strict';

var BasicConstraints = require('./basic-constraints');
var KeyUsageExtension = require('./key-usage-extension');
var validator = require('./input-validator');
var constants = require('./constants');
var forge = require('node-forge');

module.exports = X509Certificate;

/**
 * @class X509Certificate
 * @classdesc Encapsulates an X.509 certificate. To instantiate this object, use
 *            the method {@link CertificateService.newX509Certificate}.
 * @property {string} publicKey The public key of the certificate, in PEM
 *           format.
 * @property {Date} notBefore The starting time of the certificate's validity.
 * @property {Date} notAfter The ending time of the certificate's validity.
 * @property {string} serialNumber The serial number of the certificate.
 * @property {string} subjectCommonName The subject common name of the
 *           certificate.
 * @property {string} subjectOrganizationalUnit The subject organizational unit
 *           of the certificate.
 * @property {string} subjectOrganization The subject organization of the
 *           certificate.
 * @property {string} subjectLocality The subject locality of the certificate.
 * @property {string} subjectCountry The subject country of the certificate.
 * @property {string} issuerCommonName The issuer common name of the
 *           certificate.
 * @property {string} issuerOrganizationalUnit The issuer organizational unit of
 *           the certificate.
 * @property {string} issuerOrganization The issuer organization of the
 *           certificate.
 * @property {string} issuerLocality The issuer locality of the certificate.
 * @property {string} issuerCountry The issuer country of the certificate.
 * @property {BasicConstraints} basicConstraints The basic constraints of the
 *           certificate.
 * @property {KeyUsageExtension} keyUsageExtension The key usage extension of
 *           the certificate.
 */
function X509Certificate(certificate) {
  var forgeCertificate_ = forge.pki.certificateFromPem(certificate, true);

  validator.checkIsDefinedAndNotNull(
      forgeCertificate_.publicKey, 'Public key of certificate');
  this.publicKey = forge.pki.publicKeyToPem(forgeCertificate_.publicKey, 64);

  validator.checkIsDefinedAndNotNull(
      forgeCertificate_.validity.notBefore,
      'Starting time of validity of certificate');
  this.notBefore = forgeCertificate_.validity.notBefore;

  validator.checkIsDefinedAndNotNull(
      forgeCertificate_.validity.notAfter,
      'Ending time of validity of certificate');
  this.notAfter = forgeCertificate_.validity.notAfter;

  this.serialNumber = forgeCertificate_.serialNumber;
  if (!this.serialNumber) {
    this.serialNumber = '';
  }

  var field = forgeCertificate_.subject.getField({name: 'commonName'});
  this.subjectCommonName = (field) ? field.value : '';
  field = forgeCertificate_.subject.getField({shortName: 'OU'});
  this.subjectOrganizationalUnit = (field) ? field.value : '';
  field = forgeCertificate_.subject.getField({name: 'organizationName'});
  this.subjectOrganization = (field) ? field.value : '';
  field = forgeCertificate_.subject.getField({name: 'localityName'});
  this.subjectLocality = (field) ? field.value : '';
  field = forgeCertificate_.subject.getField({name: 'countryName'});
  this.subjectCountry = (field) ? field.value : '';

  field = forgeCertificate_.issuer.getField({name: 'commonName'});
  this.issuerCommonName = (field) ? field.value : '';
  field = forgeCertificate_.issuer.getField({shortName: 'OU'});
  this.issuerOrganizationalUnit = (field) ? field.value : '';
  field = forgeCertificate_.issuer.getField({name: 'organizationName'});
  this.issuerOrganization = (field) ? field.value : '';
  field = forgeCertificate_.issuer.getField({name: 'localityName'});
  this.issuerLocality = (field) ? field.value : '';
  field = forgeCertificate_.issuer.getField({name: 'countryName'});
  this.issuerCountry = (field) ? field.value : '';

  var constraints = forgeCertificate_.getExtension({name: 'basicConstraints'});
  this.basicConstraints =
      (constraints) ? new BasicConstraints(constraints) : null;

  var extension = forgeCertificate_.getExtension({name: 'keyUsage'});
  this.keyUsageExtension =
      (extension) ? new KeyUsageExtension(extension) : null;

  /**
   * Verifies that the certificate provided as input was signed by this
   * certificate.
   *
   * @function verify
   * @memberof X509Certificate
   * @param {string}
   *            certificate The certificate whose signature is to be verified,
   *            in PEM format.
   * @returns {boolean} <code>true</code> if the certificate provided as
   *          input was signed by this certificate, <code>false</code>
   *          otherwise.
   * @throws {Error}
   *             If the input data validation fails.
   */
  this.verify = function(certificate) {
    validator.checkIsNonEmptyString(
        certificate,
        'Certificate, in PEM format, containing signature to be verified');

    return forgeCertificate_.verify(forge.pki.certificateFromPem(certificate));
  };

  /**
   * Serializes this certificate into its PEM string representation.
   *
   * @function toPem
   * @memberof X509Certificate
   * @returns {string} The PEM string representation of this certificate.
   */
  this.toPem = function() {
    return forge.pki.certificateToPem(
        forgeCertificate_, constants.PEM_LINE_LENGTH);
  };

  return Object.freeze(this);
}
