/*
   Copyright (C) 2020 MariaDB Corporation

   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; version 2 of
   the License.

   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, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA.
*/

#include <iostream>

#include "mcs_int128.h"
#include "exceptclasses.h"

namespace datatypes
{
uint8_t TSInt128::printPodParts(char* buf, const int128_t& high, const int128_t& mid,
                                const int128_t& low) const
{
  char* p = buf;
  // pod[0] is low 8 bytes, pod[1] is high 8 bytes
  const uint64_t* high_pod = reinterpret_cast<const uint64_t*>(&high);
  const uint64_t* mid_pod = reinterpret_cast<const uint64_t*>(&mid);
  const uint64_t* low_pod = reinterpret_cast<const uint64_t*>(&low);

  if (high_pod[0] != 0)
  {
    p += sprintf(p, "%lu", high_pod[0]);
    p += sprintf(p, "%019lu", mid_pod[0]);
    p += sprintf(p, "%019lu", low_pod[0]);
  }
  else if (mid_pod[0] != 0)
  {
    p += sprintf(p, "%lu", mid_pod[0]);
    p += sprintf(p, "%019lu", low_pod[0]);
  }
  else
  {
    p += sprintf(p, "%lu", low_pod[0]);
  }
  return p - buf;
}

//    This method writes unsigned integer representation of TSInt128
uint8_t TSInt128::writeIntPart(const int128_t& x, char* buf, const uint8_t buflen) const
{
  char* p = buf;
  int128_t high = 0, mid = 0, low = 0;
  uint64_t maxUint64divisor = 10000000000000000000ULL;

  low = x % maxUint64divisor;
  int128_t value = x / maxUint64divisor;
  mid = value % maxUint64divisor;
  high = value / maxUint64divisor;

  p += printPodParts(p, high, mid, low);
  uint8_t written = p - buf;
  if (buflen <= written)
  {
    throw logging::QueryDataExcept("TSInt128::writeIntPart() char buffer overflow.", logging::formatErr);
  }

  return written;
}

//    conversion to std::string
std::string TSInt128::toString() const
{
  if (isNull())
  {
    return std::string("NULL");
  }

  if (isEmpty())
  {
    return std::string("EMPTY");
  }

  int128_t tempValue = s128Value;
  char buf[TSInt128::MAXLENGTH16BYTES];
  uint8_t left = sizeof(buf);
  char* p = buf;
  // sign
  if (tempValue < static_cast<int128_t>(0))
  {
    *p++ = '-';
    tempValue *= -1;
    left--;
  }
  // integer part
  // reduce the size by one to account for \0
  left--;
  p += writeIntPart(tempValue, p, left);
  *p = '\0';

  return std::string(buf);
}

std::ostream& operator<<(std::ostream& os, const TSInt128& x)
{
  os << x.toString();
  return os;
}

}  // end of namespace datatypes
