#pragma once

#include <memory>

#include "Params.hh"

#include "security_db.hh"

#include "ec_keys.hh"

class OCTETSTRING;

namespace IEEE1609dot2BaseTypes {
  class HashAlgorithm;
  class Signature;}

namespace IEEE1609dot2 {
  class Ieee1609Dot2Data;
  class Ieee1609Dot2Content;
  class ToBeSignedData;
  class SignedData;
  class EncryptedData;
  class SignerIdentifier;
}

/*!
 * \class security_services
 * \brief This class provides security services for all layers as specified in TSI TS 102 723-8 and ETSI TS 103 097
 * \remark Singleton pattern
 */
class security_services {

  static constexpr unsigned int ProtocolVersion = 3;
  
  /*!
   * \brief Unique static object reference of this class
   */
  static security_services * instance;

  std::unique_ptr<ec_keys> _ec_keys;

  std::unique_ptr<security_db> _security_db;
  
  /*!
   * \brief Default private ctor
   */
  security_services();
  /*!
   * \brief Default private dtor
   */
  ~security_services() {
    if (instance != NULL) {
      delete instance;
      instance = NULL;
    }
    _ec_keys.reset(nullptr);
    _security_db.reset(nullptr);
  };
  
public: /*! \publicsection */
  /*!
   * \brief Public accessor to the single object reference
   */
  inline static security_services & get_instance() {
    if (instance == NULL) instance = new security_services();
    return *instance;
  };

  int setup(Params &p_params);
  
  /*!
   * \brief Decrypt (if required), verify and extract the unsecured payload from the provided secured payload
   * \param[in] p_secured_gn_payload The secured payload to be processed
   * \param[in] p_verify Set to true if security checks shall be applied
   * \param[in] p_unsecured_gn_payload The extracted payload
   * \return 0 on success, negative value otherwise
   */
  int verify_and_extract_gn_payload(const OCTETSTRING& p_secured_gn_payload, const bool p_verify, OCTETSTRING& p_unsecured_gn_payload, Params& p_params);

  int secure_gn_payload(const OCTETSTRING& p_unsecured_gn_payload, const bool p_add_certificate, OCTETSTRING& p_secured_gn_payload, Params& p_params);

private:
  /*!
   * \brief Decrypt (if required), verify and extract the unsecured payload from the IEEE1609dot2::Ieee1609Dot2Content data structure
   * \param[in] p_content The secured content to be processed
   * \param[in] p_verify Set to true if security checks shall be applied
   * \param[in] p_unsecured_payload The extracted payload
   * \return 0 on success, negative value otherwise
   */
  int process_ieee_1609_dot2_content(const IEEE1609dot2::Ieee1609Dot2Content& p_ieee_1609_dot2_content, const bool p_verify, OCTETSTRING& p_unsecured_payload, Params& p_params);
  int process_ieee_1609_dot2_signed_data(const IEEE1609dot2::SignedData& p_signed_data, const bool p_verify, OCTETSTRING& p_unsecured_payload, Params& p_params);
  int process_ieee_1609_dot2_encrypted_data(const IEEE1609dot2::EncryptedData& p_encrypted_data, const bool p_verify, OCTETSTRING& p_unsecured_payload, Params& p_params);
  int sign_tbs_data(const IEEE1609dot2::ToBeSignedData& p_tbs_data, const IEEE1609dot2BaseTypes::HashAlgorithm& p_hashAlgorithm, IEEE1609dot2BaseTypes::Signature& p_signature, Params& p_params);

  int hash_sha256(const OCTETSTRING& p_data, OCTETSTRING& p_hash_data);
  int hash_sha384(const OCTETSTRING& p_data, OCTETSTRING& p_hash_data);
  int sign_ecdsa_nistp256(const OCTETSTRING& p_hash, IEEE1609dot2BaseTypes::Signature& p_signature, Params& p_params);
  int verify_sign_ecdsa_nistp256(const OCTETSTRING& p_hash, const IEEE1609dot2BaseTypes::Signature& p_signature, const std::string& p_certificate_id, Params& p_params);

}; // End of class security_services
