#include "format.h"

#include <assert.h>
#include <limits.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>

char *
kissat_next_format_string (format * format)
{
  assert (format->pos < NUM_FORMAT_STRINGS);
  char *res = format->str[format->pos++];
  if (format->pos == NUM_FORMAT_STRINGS)
    format->pos = 0;
  return res;
}

static void
format_count (char *res, uint64_t w)
{
  if (w >= 128 && kissat_is_power_of_two (w))
    {
      unsigned l;
      for (l = 0; ((uint64_t) 1 << l) != w; l++)
	assert (l + 1 < 8 * sizeof (word));
      sprintf (res, "2^%u", l);
    }
  else if (w >= 1000 && !(w % 1000))
    {
      unsigned l;
      for (l = 0; !(w % 10); l++)
	w /= 10;
      sprintf (res, "%" PRIu64 "e%u", w, l);
    }
  else
    sprintf (res, "%" PRIu64, w);
}

const char *
kissat_format_count (format * format, uint64_t w)
{
  char *res = kissat_next_format_string (format);
  format_count (res, w);
  return res;
}

const char *
kissat_format_value (format * format, bool boolean, int value)
{
  if (boolean && value)
    return "true";
  if (boolean && !value)
    return "false";
  if (value == INT_MAX)
    return "INT_MAX";
  if (value == INT_MIN)
    return "INT_MIN";
  char *res = kissat_next_format_string (format);
  if (value < 0)
    {
      *res = '-';
      format_count (res + 1, ABS (value));
    }
  else
    format_count (res, value);
  return res;
}

const char *
kissat_format_bytes (format * format, uint64_t bytes)
{
  char *res = kissat_next_format_string (format);
  if (bytes < (1u << 10))
    sprintf (res, "%" PRIu64 " bytes", bytes);
  else if (bytes < (1u << 20))
    sprintf (res, "%" PRIu64 " bytes (%" PRIu64 " KB)",
	     bytes, (bytes + (1 << 9)) >> 10);
  else if (bytes < (1u << 30))
    sprintf (res, "%" PRIu64 " bytes (%" PRIu64 " MB)",
	     bytes, (bytes + (1u << 19)) >> 20);
  else
    sprintf (res, "%" PRIu64 " bytes (%" PRIu64 " GB)",
	     bytes, (bytes + (1u << 29)) >> 30);
  return res;
}

const char *
kissat_format_time (format * format, uint64_t seconds)
{
  if (!seconds)
    return "0s";
  char *res = kissat_next_format_string (format);
  uint64_t minutes = seconds / 60;
  seconds %= 60;
  uint64_t hours = minutes / 60;
  minutes %= 60;
  uint64_t days = hours / 24;
  hours %= 24;
  char *tmp = res;
  if (days)
    {
      sprintf (res, "%" PRIu64 "d", days);
      tmp += strlen (res);
    }
  if (hours)
    {
      if (tmp != res)
	*tmp++ = ' ';
      sprintf (tmp, "%" PRIu64 "h", hours);
      tmp += strlen (tmp);
    }
  if (minutes)
    {
      if (tmp != res)
	*tmp++ = ' ';
      sprintf (tmp, "%" PRIu64 "m", minutes);
      tmp += strlen (tmp);
    }
  if (seconds)
    {
      if (tmp != res)
	*tmp++ = ' ';
      sprintf (tmp, "%" PRIu64 "s", seconds);
    }
  return res;
}

const char *
kissat_format_signs (format * format, unsigned size, word signs)
{
  char *res = kissat_next_format_string (format);
  assert (size + 1 < FORMAT_STRING_SIZE);
  char *p = res;
  word bit = 1;
  for (unsigned i = 0; i < size; i++, bit <<= 1)
    *p++ = (bit & signs) ? '1' : '0';
  *p = 0;
  return res;
}

const char *
kissat_format_ordinal (format * format, uint64_t ordinal)
{
  const char *suffix;
  unsigned mod100 = ordinal % 100;
  if (10 <= mod100 && mod100 <= 19)
    suffix = "th";
  else
    {
      switch (mod100 % 10)
	{
	case 1:
	  suffix = "st";
	  break;
	case 2:
	  suffix = "nd";
	  break;
	case 3:
	  suffix = "rd";
	  break;
	default:
	  suffix = "th";
	  break;
	}
    }
  char *res = kissat_next_format_string (format);
  sprintf (res, "%" PRIu64 "%s", ordinal, suffix);
  return res;
}
