/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include 
#include 
#include 
#include "td/utils/Slice.h"
namespace digest {
struct OpensslEVP_SHA1 {
  enum { digest_bytes = 20 };
  static const EVP_MD *get_evp() {
    return EVP_sha1();
  }
};
struct OpensslEVP_SHA256 {
  enum { digest_bytes = 32 };
  static const EVP_MD *get_evp() {
    return EVP_sha256();
  }
};
struct OpensslEVP_SHA512 {
  enum { digest_bytes = 64 };
  static const EVP_MD *get_evp() {
    return EVP_sha512();
  }
};
template 
class HashCtx {
  EVP_MD_CTX *ctx{nullptr};
  void init();
  void clear();
 public:
  enum { digest_bytes = H::digest_bytes };
  HashCtx() {
    init();
  }
  HashCtx(const void *data, std::size_t len) {
    init();
    feed(data, len);
  }
  ~HashCtx() {
    clear();
  }
  void reset();
  void feed(const void *data, std::size_t len);
  void feed(td::Slice slice) {
    feed(slice.data(), slice.size());
  }
  std::size_t extract(unsigned char buffer[digest_bytes]);
  std::size_t extract(td::MutableSlice slice);
  std::string extract();
};
template 
void HashCtx::init() {
  ctx = EVP_MD_CTX_create();
  reset();
}
template 
void HashCtx::reset() {
  EVP_DigestInit_ex(ctx, H::get_evp(), 0);
}
template 
void HashCtx::clear() {
  EVP_MD_CTX_destroy(ctx);
  ctx = nullptr;
}
template 
void HashCtx::feed(const void *data, std::size_t len) {
  EVP_DigestUpdate(ctx, data, len);
}
template 
std::size_t HashCtx::extract(unsigned char buffer[digest_bytes]) {
  unsigned olen = 0;
  EVP_DigestFinal_ex(ctx, buffer, &olen);
  assert(olen == digest_bytes);
  return olen;
}
template 
std::size_t HashCtx::extract(td::MutableSlice slice) {
  return extract(slice.ubegin());
}
template 
std::string HashCtx::extract() {
  unsigned char buffer[digest_bytes];
  unsigned olen = 0;
  EVP_DigestFinal_ex(ctx, buffer, &olen);
  assert(olen == digest_bytes);
  return std::string((char *)buffer, olen);
}
typedef HashCtx SHA1;
typedef HashCtx SHA256;
typedef HashCtx SHA512;
template 
std::size_t hash_str(unsigned char buffer[T::digest_bytes], const void *data, std::size_t size) {
  T hasher(data, size);
  return hasher.extract(buffer);
}
template 
std::size_t hash_two_str(unsigned char buffer[T::digest_bytes], const void *data1, std::size_t size1, const void *data2,
                         std::size_t size2) {
  T hasher(data1, size1);
  hasher.feed(data2, size2);
  return hasher.extract(buffer);
}
template 
std::string hash_str(const void *data, std::size_t size) {
  T hasher(data, size);
  return hasher.extract();
}
template 
std::string hash_two_str(const void *data1, std::size_t size1, const void *data2, std::size_t size2) {
  T hasher(data1, size1);
  hasher.feed(data2, size2);
  return hasher.extract();
}
}  // namespace digest