/**********************************************************************
 * Copyright (c) 2020 Andrew Poelstra                                 *
 * Distributed under the MIT software license, see the accompanying   *
 * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
 **********************************************************************/

#ifndef SECP256K1_MODULE_BPPP_MAIN_IMPL_H
#define SECP256K1_MODULE_BPPP_MAIN_IMPL_H

#include "../../../include/secp256k1_bppp.h"
#include "../../../include/secp256k1_generator.h"
#include "../generator/main_impl.h" /* for generator_{load, save} */
#include "../../hash.h"
#include "../../util.h"
#include "../bppp/main.h"
#include "../bppp/bppp_norm_product_impl.h"

secp256k1_bppp_generators *secp256k1_bppp_generators_create(const secp256k1_context *ctx, size_t n) {
    secp256k1_bppp_generators *ret;
    secp256k1_rfc6979_hmac_sha256 rng;
    const secp256k1_hash_ctx *hash_ctx = secp256k1_get_hash_context(ctx);
    unsigned char seed[64];
    size_t i;

    VERIFY_CHECK(ctx != NULL);

    ret = checked_malloc(&ctx->error_callback, sizeof(*ret));
    if (ret == NULL) {
        return NULL;
    }
    /* Ensure that multiplication will not wrap around */
    if (n > SIZE_MAX / sizeof(*ret->gens)) {
        free(ret);
        return NULL;
    }
    ret->gens = checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens));
    if (ret->gens == NULL) {
        free(ret);
        return NULL;
    }
    ret->n = n;

    secp256k1_fe_get_b32(&seed[0], &secp256k1_ge_const_g.x);
    secp256k1_fe_get_b32(&seed[32], &secp256k1_ge_const_g.y);

    secp256k1_rfc6979_hmac_sha256_initialize(hash_ctx, &rng, seed, 64);
    for (i = 0; i < n; i++) {
        secp256k1_generator gen;
        unsigned char tmp[32] = { 0 };
        secp256k1_rfc6979_hmac_sha256_generate(hash_ctx, &rng, tmp, 32);
        CHECK(secp256k1_generator_generate(ctx, &gen, tmp));
        secp256k1_generator_load(&ret->gens[i], &gen);
    }

    return ret;
}

secp256k1_bppp_generators* secp256k1_bppp_generators_parse(const secp256k1_context* ctx, const unsigned char* data, size_t data_len) {
    size_t n = data_len / 33;
    secp256k1_bppp_generators* ret;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(data != NULL);

    if (data_len % 33 != 0) {
        return NULL;
    }

    ret = checked_malloc(&ctx->error_callback, sizeof(*ret));
    if (ret == NULL) {
        return NULL;
    }
    ret->n = n;
    ret->gens = checked_malloc(&ctx->error_callback, n * sizeof(*ret->gens));
    if (ret->gens == NULL) {
        free(ret);
        return NULL;
    }

    while (n--) {
        secp256k1_generator gen;
        if (!secp256k1_generator_parse(ctx, &gen, &data[33 * n])) {
            free(ret->gens);
            free(ret);
            return NULL;
        }
        secp256k1_generator_load(&ret->gens[n], &gen);
    }
    return ret;
}

int secp256k1_bppp_generators_serialize(const secp256k1_context* ctx, const secp256k1_bppp_generators* gens, unsigned char* data, size_t *data_len) {
    size_t i;

    VERIFY_CHECK(ctx != NULL);
    ARG_CHECK(gens != NULL);
    ARG_CHECK(data != NULL);
    ARG_CHECK(data_len != NULL);
    ARG_CHECK(*data_len >= 33 * gens->n);

    memset(data, 0, *data_len);
    for (i = 0; i < gens->n; i++) {
        secp256k1_generator gen;
        secp256k1_generator_save(&gen, &gens->gens[i]);
        secp256k1_generator_serialize(ctx, &data[33 * i], &gen);
    }

    *data_len = 33 * gens->n;
    return 1;
}

void secp256k1_bppp_generators_destroy(const secp256k1_context* ctx, secp256k1_bppp_generators *gens) {
    VERIFY_CHECK(ctx != NULL);
    (void) ctx;
    if (gens != NULL) {
        free(gens->gens);
        free(gens);
    }
}

#endif
