#include <assert.h>
#include <string.h>

#include "checkpoint.h"

/* Formats digits right-justified in buffer. */
static size_t
format_decimal_right (uint8_t buf[CHECKPOINT_DECIMAL_MAX_SIZE], sigsum_size_t x)
{
  assert ((x >> 63) == 0);

  if (x == 0)
    {
      buf[CHECKPOINT_DECIMAL_MAX_SIZE-1] = '0';
      return 1;
    }
  size_t i;
  for (i = 0; x > 0; i++, x /= 10)
    {
      assert (i < CHECKPOINT_DECIMAL_MAX_SIZE);
      buf[CHECKPOINT_DECIMAL_MAX_SIZE - 1 - i] = '0' + (x % 10);
    }
  return i;
}

/* Potentially clobbers all of the out area. */
static size_t
format_decimal (uint8_t out[CHECKPOINT_DECIMAL_MAX_SIZE], sigsum_size_t x)
{
  size_t size = format_decimal_right (out, x);
  if (size < CHECKPOINT_DECIMAL_MAX_SIZE)
    memmove (out, out + CHECKPOINT_DECIMAL_MAX_SIZE - size, size);
  return size;
}

#define HEX_DIGEST_SIZE 64
static void
format_hex_digest (uint8_t out[HEX_DIGEST_SIZE], const struct sigsum_hash *digest)
{
  static const char digits[16] = "0123456789abcdef";
  for (size_t i = 0; i < sizeof (digest->h); i++)
    {
      out[2*i] = digits[digest->h[i] >> 4];
      out[2*i+1] = digits[digest->h[i] &0xf];
    }
}

#define BASE64_DIGEST_SIZE 44
static void
format_base64_digest (uint8_t out[BASE64_DIGEST_SIZE], const struct sigsum_hash *digest)
{
  static const char digits[64] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789+/";

  size_t i, j;
  unsigned w, bits;
  for (i = j = w = bits = 0; i < sizeof (digest->h); i++)
    {
      w = (w << 8) | digest->h[i];
      bits += 2; /* Excess bits with 8 bits in and 6 bits out. */
      out[j++] = digits[(w >> bits) & 0x3f];
      if (bits == 6)
	{
	  bits = 0;
	  out[j++] = digits[w & 0x3f];
	}
    }

  assert (j == BASE64_DIGEST_SIZE - 2);
  assert (bits == 4);
  out[j++] = digits[(w << 2) & 0x3f];
  out[j++] = '=';
}

size_t
_sigsum_checkpoint_body (uint8_t out[CHECKPOINT_BODY_MAX_SIZE],
			 const struct sigsum_hash *key_hash,
			 const struct sigsum_tree_head *tree_head)
{
  static const uint8_t sigsum_origin_prefix[19] = "sigsum.org/v1/tree/";
  memcpy (out, sigsum_origin_prefix, sizeof (sigsum_origin_prefix));
  size_t size = sizeof (sigsum_origin_prefix);
  format_hex_digest (out + size, key_hash);
  size += HEX_DIGEST_SIZE;
  out[size++] = '\n';
  size += format_decimal (out + size, tree_head->size);
  out[size++] = '\n';
  format_base64_digest (out + size, &tree_head->root_hash);
  size += BASE64_DIGEST_SIZE;
  out[size++] = '\n';
  return size;
}

size_t
_sigsum_cosignature_prefix (uint8_t out[COSIGNATURE_PREFIX_MAX_SIZE],
			    uint64_t timestamp)
{
  static const uint8_t cosignature_prefix[20] = "cosignature/v1\ntime ";
  size_t i = COSIGNATURE_PREFIX_MAX_SIZE;
  out[--i] = '\n';
  i -= format_decimal_right (out + i - CHECKPOINT_DECIMAL_MAX_SIZE, timestamp);
  assert (i >= sizeof (cosignature_prefix));
  i -= sizeof (cosignature_prefix);
  memcpy (out + i, cosignature_prefix, sizeof (cosignature_prefix));
  return COSIGNATURE_PREFIX_MAX_SIZE - i;
}
