/*
     This file is part of GNUnet.
     Copyright (C) 2014 GNUnet e.V.

     GNUnet is free software: you can redistribute it and/or modify it
     under the terms of the GNU Affero General Public License as published
     by the Free Software Foundation, either version 3 of the License,
     or (at your option) any later version.

     GNUnet 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
     Affero General Public License for more details.

     You should have received a copy of the GNU Affero General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

     SPDX-License-Identifier: AGPL3.0-or-later
 */

#include "secretsharing.h"

/**
 * Read a share from its binary representation.
 *
 * @param data Binary representation of the share.
 * @param len Length of @a data.
 * @param[out] readlen Number of bytes read,
 *             ignored if NULL.
 * @return The share, or NULL on error.
 */
struct GNUNET_SECRETSHARING_Share *
GNUNET_SECRETSHARING_share_read (const void *data,
                                 size_t len,
                                 size_t *readlen)
{
  struct GNUNET_SECRETSHARING_Share *share;
  const struct GNUNET_SECRETSHARING_ShareHeaderNBO *sh = data;
  const char *p;
  size_t n;
  uint16_t payload_size;

  payload_size = ntohs (sh->num_peers)
                 * (sizeof(uint16_t) + sizeof(struct
                                              GNUNET_SECRETSHARING_FieldElement)
                    + sizeof(struct GNUNET_PeerIdentity));

  if (NULL != readlen)
    *readlen = payload_size + sizeof *sh;

  share = GNUNET_new (struct GNUNET_SECRETSHARING_Share);

  share->threshold = ntohs (sh->threshold);
  share->num_peers = ntohs (sh->num_peers);
  share->my_peer = ntohs (sh->my_peer);

  share->my_share = sh->my_share;
  share->public_key = sh->public_key;

  p = (const char *) &sh[1];

  n = share->num_peers * sizeof(struct GNUNET_PeerIdentity);
  share->peers = GNUNET_new_array (share->num_peers,
                                   struct GNUNET_PeerIdentity);
  GNUNET_memcpy (share->peers, p, n);
  p += n;

  n = share->num_peers * sizeof(struct GNUNET_SECRETSHARING_FieldElement);
  share->sigmas = GNUNET_new_array (share->num_peers,
                                    struct GNUNET_SECRETSHARING_FieldElement);
  GNUNET_memcpy (share->sigmas, p, n);
  p += n;

  n = share->num_peers * sizeof(uint16_t);
  share->original_indices = GNUNET_new_array (share->num_peers,
                                              uint16_t);
  GNUNET_memcpy (share->original_indices, p, n);

  return share;
}


/**
 * Convert a share to its binary representation.
 * Can be called with a NULL @a buf to get the size of the share.
 *
 * @param share Share to write.
 * @param buf Buffer to write to.
 * @param buflen Number of writable bytes in @a buf.
 * @param[out] writelen Pointer to store number of bytes written,
 *             ignored if NULL.
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure.
 */
int
GNUNET_SECRETSHARING_share_write (const struct
                                  GNUNET_SECRETSHARING_Share *share,
                                  void *buf, size_t buflen, size_t *writelen)
{
  uint16_t payload_size;
  struct GNUNET_SECRETSHARING_ShareHeaderNBO *sh;
  char *p;
  int n;

  payload_size = share->num_peers
                 * (sizeof(uint16_t) + sizeof(struct
                                              GNUNET_SECRETSHARING_FieldElement)
                    + sizeof(struct GNUNET_PeerIdentity));

  if (NULL != writelen)
    *writelen = payload_size + sizeof(struct
                                      GNUNET_SECRETSHARING_ShareHeaderNBO);

  /* just a query for the writelen */
  if (buf == NULL)
    return GNUNET_OK;

  /* wrong buffer size */
  if (buflen < payload_size + sizeof(struct
                                     GNUNET_SECRETSHARING_ShareHeaderNBO))
    return GNUNET_SYSERR;

  sh = buf;

  sh->threshold = htons (share->threshold);
  sh->num_peers = htons (share->num_peers);
  sh->my_peer = htons (share->my_peer);

  sh->my_share = share->my_share;
  sh->public_key = share->public_key;

  p = (void *) &sh[1];

  n = share->num_peers * sizeof(struct GNUNET_PeerIdentity);
  GNUNET_memcpy (p, share->peers, n);
  p += n;

  n = share->num_peers * sizeof(struct GNUNET_SECRETSHARING_FieldElement);
  GNUNET_memcpy (p, share->sigmas, n);
  p += n;

  n = share->num_peers * sizeof(uint16_t);
  GNUNET_memcpy (p, share->original_indices, n);

  return GNUNET_OK;
}


void
GNUNET_SECRETSHARING_share_destroy (struct GNUNET_SECRETSHARING_Share *share)
{
  GNUNET_free (share->original_indices);
  share->original_indices = NULL;
  GNUNET_free (share->sigmas);
  share->sigmas = NULL;
  GNUNET_free (share->peers);
  share->peers = NULL;
  GNUNET_free (share);
}
