#pragma once

#include <vector>

#include <openssl/err.h>
#include <openssl/ec.h>
#include <openssl/pem.h>
#include <openssl/bn.h>

/*!
 * \enum Supported Elliptic curves
 */
enum class ec_elliptic_curves: unsigned int {
  nist_p_256,         /*!< NIST P-256, P-256, primve256v1 */
  brainpool_p_256_r1, /*!< Brainpool P256r1 */
  brainpool_p_384_r1  /*!< Brainpool P384r1 */
}; // End of class ec_elliptic_curves

/*!
 * \class This class implements the generation of a key pair private/public
 */
class ec_keys {
  ec_elliptic_curves _elliptic_curve;   /*!< Selected elleptic curve */
  EC_KEY* _ec_key;                      /*!< EC_KEY reference */
  const EC_GROUP* _ec_group;            /*!< EC_GROUP reference */
  BN_CTX* _bn_ctx;                      /*!< Pre-alocated memory used to increase OpenSSL processing */
  std::vector<unsigned char> _pr_key;   /*!< Private key storage */
  std::vector<unsigned char> _pu_key_x; /*!< Public key X storage */
  std::vector<unsigned char> _pu_key_y; /*!< Public key Y storage */
  
public:
  /*!
   * \brief Default constructor
   * \param[in] p_elliptic_curve The ECDSA curve family to be used
   * \remark Call the method ec_keys::generate() to initialise the private/publi kaey pair
   */ 
  ec_keys(const ec_elliptic_curves p_elliptic_curve);
  /*!
   * \brief Constructor based on the private key only
   * \param[in] p_elliptic_curve The ECDSA curve family to be used
   * \param[in] p_private_key The private key
   * \remark Some public key are created based on the provided private keys
   */ 
  ec_keys(const ec_elliptic_curves p_elliptic_curve, const std::vector<unsigned char>& p_private_key);
  /*!
   * \brief Constructor based on the public keys only
   * \param[in] p_elliptic_curve The ECDSA curve family to be used
   * \remark The call to the method ec_keys::sign() will failed
   * \remark The call to the method ec_keys::generate() will overwrite the provided public keys
   */ 
  ec_keys(const ec_elliptic_curves p_elliptic_curve, const std::vector<unsigned char>& p_public_key_x, const std::vector<unsigned char>& p_public_key_y);
  /*!
   * \brief Destructor
   */ 
  virtual ~ec_keys();

  int generate();
  int sign(const std::vector<unsigned char>& p_data, std::vector<unsigned char>& p_r_sig, std::vector<unsigned char>& p_s_sig);
  int sign_verif(const std::vector<unsigned char>& p_data, const std::vector<unsigned char>& p_signature);
  
  inline const std::vector<unsigned char>& private_key() const { return _pr_key; };
  inline const std::vector<unsigned char>& public_key_x() const { return _pu_key_x; };
  inline const std::vector<unsigned char>& public_key_y() const { return _pu_key_y; };

private:
  const int init();
}; // End of class ec_keys
