#include <string.h>

#include "ascii.h"
#include "ascii-internal.h"

/* Case-insensitive. */
static int
hex_digit (char c)
{
  if (c >= '0' && c <= '9')
    return c - '0';
  if (c >= 'a' && c <= 'f')
    return c - ('a' - 10);
  if (c >= 'A' && c <= 'F')
    return c - ('A' - 10);
  return -1;
}

bool
_sigsum_decode_hex (size_t size, uint8_t *dst, const char *src)
{
  size_t i;
  for (i = 0; i < size; i++)
    {
      int hi, lo;
      if ((hi = hex_digit (src[2*i])) < 0)
	return false;
      if ((lo = hex_digit (src[2*i + 1])) < 0)
	return false;
      dst[i] = ((hi << 4) | lo);
    }
  return true;
}

bool
sigsum_decode_hex_key (struct sigsum_pubkey *dst, const char *src)
{
  return _sigsum_decode_hex (sizeof (dst->k), dst->k, src);
}

bool
sigsum_decode_hex_hash (struct sigsum_hash *dst, const char *src)
{
  return _sigsum_decode_hex (sizeof (dst->h), dst->h, src);
}

bool
sigsum_decode_hex_signature (struct sigsum_signature *dst, const char *src)
{
  return _sigsum_decode_hex (sizeof (dst->s), dst->s, src);
}

static int
decimal_digit (char c)
{
  if (c >= '0' && c <= '9')
    return c - '0';
  return -1;
}

bool
sigsum_decode_decimal (sigsum_size_t *number, size_t size, const char *data)
{
  sigsum_size_t x;
  int digit;

  if (!size)
    return false;
  if ((digit = decimal_digit (data[0])) < 0)
    return false;

  x = digit;

  /* No leading zeros. */
  if (x == 0 && size > 1)
    return false;

  for (size_t i = 1; i < size; i++)
    {
      if (x > (~(sigsum_size_t) 0) / 20)
	/* Multiplying by 10 would exceeed 2^63 - 1 */
	return false;
      if ((digit = decimal_digit (data[i])) < 0)
	return false;
      x = x*10 + digit;
      if (x >> 63)
	return false;
    }

  *number = x;
  return true;
}

bool
_sigsum_buffer_line_strict (struct sigsum_buffer *buffer, struct sigsum_buffer *value)
{
  const char *eol;
  size_t line_length;

  eol = memchr (buffer->data, '\n', buffer->size);
  if (!eol)
    return 0;

  line_length = eol - buffer->data;
  value->data = buffer->data;
  value->size = line_length;

  buffer->data += line_length + 1;
  buffer->size -= line_length + 1;

  return 1;
}

void
_sigsum_buffer_line (struct sigsum_buffer *buffer, struct sigsum_buffer *value)
{
  if (_sigsum_buffer_line_strict (buffer, value))
    return;
  *value = *buffer;

  buffer->data += buffer->size;
  buffer->size = 0;
}

static bool
is_space (char c)
{
  return c == ' ' || c == '\t';
}

void
_sigsum_buffer_token (struct sigsum_buffer *buffer, struct sigsum_buffer *token)
{
  /* Trim leading space. */
  while (buffer->size > 0 && is_space (buffer->data[0]))
    { buffer->size--; buffer->data++; }

  if (!buffer->size)
    {
      token->data = NULL;
      token->size = 0;
      return;
    }

  token->data = buffer->data;

  for (size_t i = 0; i < buffer->size; i++)
    if (is_space (buffer->data[i]))
      {
	/* Space terminated token. Also skip a space character. */
	token->size = i++;
	buffer->size -= i;
	buffer->data += i;
	return;
      }
  /* Token extends to end of buffer */
  token->size = buffer->size;
  buffer->data += buffer->size;
  buffer->size = 0;
}
