/* Copyright (C) 2021-2023 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

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

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <values.h>
#include <stdarg.h>

#include "gp-defs.h"
#include "StringBuilder.h"
#include "i18n.h"

StringBuilder::StringBuilder ()
{
  count = 0;
  maxCapacity = 16;
  value = (char *) malloc (maxCapacity);
  memset (value, 0, maxCapacity);
}

StringBuilder::StringBuilder (int capacity)
{
  count = 0;
  maxCapacity = capacity;
  value = (char *) malloc (maxCapacity);
  memset (value, 0, maxCapacity);
}

StringBuilder::~StringBuilder ()
{
  free (value);
}

void
StringBuilder::ensureCapacity (int minimumCapacity)
{
  if (minimumCapacity > maxCapacity)
    expandCapacity (minimumCapacity);
}

void
StringBuilder::expandCapacity (int minimumCapacity)
{
  int newCapacity = (maxCapacity + 1) * 2;
  if (newCapacity < 0)
    newCapacity = MAXINT;
  else if (minimumCapacity > newCapacity)
    newCapacity = minimumCapacity;
  char *newValue = (char *) malloc (newCapacity);
  maxCapacity = newCapacity;
  memcpy (newValue, value, count);
  memset (newValue + count, 0, maxCapacity - count);
  free (value);
  value = newValue;
}

void
StringBuilder::trimToSize ()
{
  if (count < maxCapacity)
    {
      char *newValue = (char *) malloc (count);
      maxCapacity = count;
      memcpy (newValue, value, count);
      free (value);
      value = newValue;
    }
}

void
StringBuilder::trim ()
{
  while (count > 0)
    {
      if (value[count - 1] != ' ')
	break;
      count--;
    }
}

void
StringBuilder::setLength (int newLength)
{
  if (newLength < 0)
    return;
  if (newLength > maxCapacity)
    expandCapacity (newLength);
  if (count < newLength)
    {
      for (; count < newLength; count++)
	value[count] = '\0';
    }
  else
    count = newLength;
}

char
StringBuilder::charAt (int index)
{
  if (index < 0 || index >= count)
    return 0;
  return value[index];
}

void
StringBuilder::getChars (int srcBegin, int srcEnd, char dst[], int dstBegin)
{
  if (srcBegin < 0)
    return;
  if (srcEnd < 0 || srcEnd > count)
    return;
  if (srcBegin > srcEnd)
    return;
  memcpy (dst + dstBegin, value + srcBegin, srcEnd - srcBegin);
}

void
StringBuilder::setCharAt (int index, char ch)
{
  if (index < 0 || index >= count)
    return;
  value[index] = ch;
}

StringBuilder *
StringBuilder::append (StringBuilder *sb)
{
  if (sb == NULL)
    return append (NTXT ("null"));
  int len = sb->count;
  int newcount = count + len;
  if (newcount > maxCapacity)
    expandCapacity (newcount);
  sb->getChars (0, len, value, count);
  count = newcount;
  return this;
}

StringBuilder *
StringBuilder::append (const char str[])
{
  int len = (int) strlen (str);
  int newCount = count + len;
  if (newCount > maxCapacity)
    expandCapacity (newCount);
  memcpy (value + count, str, len);
  count = newCount;
  return this;
}

StringBuilder *
StringBuilder::append (const char str[], int offset, int len)
{
  int newCount = count + len;
  if (newCount > maxCapacity)
    expandCapacity (newCount);
  memcpy (value + count, str + offset, len);
  count = newCount;
  return this;
}

StringBuilder *
StringBuilder::append (bool b)
{
  if (b)
    append (NTXT ("true"));
  else
    append (NTXT ("false"));
  return this;
}

StringBuilder *
StringBuilder::append (char c)
{
  int newCount = count + 1;
  if (newCount > maxCapacity)
    {
      expandCapacity (newCount);
    }
  value[count++] = c;
  return this;
}

StringBuilder *
StringBuilder::append (int i)
{
  char buf[16];
  snprintf (buf, sizeof (buf), NTXT ("%d"), i);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (unsigned int i)
{
  char buf[16];
  snprintf (buf, sizeof (buf), NTXT ("%u"), i);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (long lng)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%ld"), lng);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (unsigned long lng)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%lu"), lng);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (long long lng)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%lld"), lng);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (unsigned long long lng)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%llu"), lng);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (float f)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%f"), (double) f);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::append (double d)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%f"), d);
  append (buf);
  return this;
}

StringBuilder *
StringBuilder::_delete (int start, int end)
{
  if (start < 0)
    return this;
  if (end > count)
    end = count;
  if (start > end)
    return this;
  int len = end - start;
  if (len > 0)
    {
      memcpy (value + start, value + start + len, count - end);
      count -= len;
    }
  return this;
}

StringBuilder *
StringBuilder::deleteCharAt (int index)
{
  if (index < 0 || index >= count)
    return this;
  memcpy (value + index, value + index + 1, count - index - 1);
  count--;
  return this;
}

bool
StringBuilder::endsWith (const char str[])
{
  if (str == NULL)
    {
      if (count == 0)
	return true;
      return false;
    }
  int len = (int) strlen (str);
  if (len == 0)
    return true;
  int start = count - len;
  if (start < 0)
    return false;
  int res = strncmp ((const char *) (value + start), str, len);
  if (res != 0)
    return false;
  return true;
}

StringBuilder *
StringBuilder::insert (int index, const char str[], int offset, int len)
{
  if (index < 0 || index > count)
    return this;
  if (offset < 0 || len < 0 || offset > ((int) strlen (str)) - len)
    return this;
  int newCount = count + len;
  if (newCount > maxCapacity)
    expandCapacity (newCount);
  memcpy (value + index + len, value + index, count - index);
  memcpy (value + index, str + offset, len);
  count = newCount;
  return this;
}

StringBuilder *
StringBuilder::insert (int offset, const char str[])
{
  if (offset < 0 || offset > count)
    return this;
  int len = (int) strlen (str);
  int newCount = count + len;
  if (newCount > maxCapacity)
    expandCapacity (newCount);
  memcpy (value + offset + len, value + offset, count - offset);
  memcpy (value + offset, str, len);
  count = newCount;
  return this;
}

StringBuilder *
StringBuilder::insert (int offset, bool b)
{
  return insert (offset, b ? NTXT ("true") : NTXT ("false"));
}

StringBuilder *
StringBuilder::insert (int offset, char c)
{
  int newCount = count + 1;
  if (newCount > maxCapacity)
    expandCapacity (newCount);
  memcpy (value + offset + 1, value + offset, count - offset);
  value[offset] = c;
  count = newCount;
  return this;
}

StringBuilder *
StringBuilder::insert (int offset, int i)
{
  char buf[16];
  snprintf (buf, sizeof (buf), NTXT ("%d"), i);
  insert (offset, buf);
  return this;
}

StringBuilder *
StringBuilder::insert (int offset, long l)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%ld"), l);
  insert (offset, buf);
  return this;
}

StringBuilder *
StringBuilder::insert (int offset, float f)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%f"), (double) f);
  insert (offset, buf);
  return this;
}

StringBuilder *
StringBuilder::insert (int offset, double d)
{
  char buf[32];
  snprintf (buf, sizeof (buf), NTXT ("%f"), d);
  insert (offset, buf);
  return this;
}

StringBuilder *
StringBuilder::reverse ()
{
  int n = count - 1;
  for (int j = (n - 1) >> 1; j >= 0; --j)
    {
      char temp = value[j];
      char temp2 = value[n - j];
      value[j] = temp2;
      value[n - j] = temp;
    }
  return this;
}

//String *StringBuilder::toString();
char *
StringBuilder::toString ()
{
  char *str = (char *) malloc (count + 1);
  memcpy (str, value, count);
  str[count] = '\0';
  return str;
}

void
StringBuilder::toFile (FILE *fp)
{
  append ('\0');
  count--;
  fprintf (fp, NTXT ("%s"), value);
}

void
StringBuilder::toFileLn (FILE *fp)
{
  trim ();
  append ('\0');
  count--;
  fprintf (fp, NTXT ("%s\n"), value);
}

StringBuilder *
StringBuilder::sprintf (const char *fmt, ...)
{
  int cnt;
  setLength (0);

  va_list vp;
  va_start (vp, fmt);
  cnt = vsnprintf (value, maxCapacity, fmt, vp);
  va_end (vp);
  if (cnt < maxCapacity)
    {
      count = cnt;
      return this;
    }

  // Have to count the trailing zero
  ensureCapacity (cnt + 1);
  va_start (vp, fmt);
  count = vsnprintf (value, maxCapacity, fmt, vp);
  va_end (vp);
  return this;
}

StringBuilder *
StringBuilder::appendf (const char *fmt, ...)
{
  va_list vp;
  va_start (vp, fmt);
  int cnt = vsnprintf (value + count, maxCapacity - count, fmt, vp);
  va_end (vp);
  if (cnt + count < maxCapacity)
    {
      count += cnt;
      return this;
    }

  // Have to count the trailing zero
  ensureCapacity (count + cnt + 1);
  va_start (vp, fmt);
  count += vsnprintf (value + count, maxCapacity - count, fmt, vp);
  va_end (vp);
  return this;
}

int
StringBuilder::indexOf (const char str[])
{
  return indexOf (str, 0);
}

int
StringBuilder::indexOf (const char str[], int fromIndex)
{
  int len = (int) strlen (str);
  if (fromIndex >= count)
    return len == 0 ? count : -1;
  if (fromIndex < 0)
    fromIndex = 0;
  if (len == 0)
    return fromIndex;

  char first = str[0];
  int max = (count - len);

  for (int i = fromIndex; i <= max; i++)
    {
      /* Look for first character. */
      if (value[i] != first)
	while (++i <= max && value[i] != first)
	  ;
      /* Found first character, now look at the rest of v2 */
      if (i <= max)
	{
	  int j = i + 1;
	  int end = j + len - 1;
	  for (int k = 1; j < end && value[j] == str[k]; j++, k++)
	    ;
	  if (j == end)     /* Found whole string. */
	    return i;
	}
    }
  return -1;
}

int
StringBuilder::lastIndexOf (const char str[])
{
  return lastIndexOf (str, count);
}

int
StringBuilder::lastIndexOf (const char str[], int fromIndex)
{
  /*
   * Check arguments; return immediately where possible. For
   * consistency, don't check for null str.
   */
  int len = (int) strlen (str);
  int rightIndex = count - len;
  if (fromIndex < 0)
    return -1;
  if (fromIndex > rightIndex)
    fromIndex = rightIndex;
  /* Empty string always matches. */
  if (len == 0)
    return fromIndex;

  int strLastIndex = len - 1;
  char strLastChar = str[strLastIndex];
  int min = len - 1;
  int i = min + fromIndex;

  while (true)
    {
      while (i >= min && value[i] != strLastChar)
	i--;
      if (i < min)
	return -1;

      int j = i - 1;
      int start = j - (len - 1);
      int k = strLastIndex - 1;
      while (j > start)
	{
	  if (value[j--] != str[k--])
	    {
	      i--;
	      break;
	    }
	}
      if (j == start)
	return start + 1;
    }
}

