/* KInterbasDB Python Package - Implementation of Parameter Conversion DB->Py
 *
 * Version 3.3
 *
 * The following contributors hold Copyright (C) over their respective
 * portions of code (see license.txt for details):
 *
 * [Original Author (maintained through version 2.0-0.3.1):]
 *   1998-2001 [alex]  Alexander Kuznetsov   <alexan@users.sourceforge.net>
 * [Maintainers (after version 2.0-0.3.1):]
 *   2001-2002 [maz]   Marek Isalski         <kinterbasdb@maz.nu>
 *   2002-2007 [dsr]   David Rushby          <woodsplitter@rocketmail.com>
 * [Contributors:]
 *   2001      [eac]   Evgeny A. Cherkashin  <eugeneai@icc.ru>
 *   2001-2002 [janez] Janez Jere            <janez.jere@void.si>
 */

/* This source file is designed to be directly included in _kiconversion.c,
 * without the involvement of a header file. */

/* The undocumented portion of ISC_TIME is ten-thousandths of a second: */
#define MICROSECONDS_FROM_ISC_TIME(t) \
  (((t) % 10000) * 100)

#define MICROSECONDS_FROM_ISC_TIMESTAMP_PTR(tp) \
  (((tp)->timestamp_time % 10000) * 100)


static PyObject *conv_out_char(char *data, size_t size) {
  return PyString_FromStringAndSize(data, SIZE_T_TO_PYTHON_SIZE(size));
} /* conv_out_char */


static PyObject *conv_out_varchar(char *data) {
  /* The first sizeof(short) bytes contain the size of the string. */
  return PyString_FromStringAndSize(
      data + sizeof(short),
      (int) *( (short *)data )
    );
} /* conv_out_varchar */


static PyObject *_conv_out_integer_types(
    PyObject *py_raw, boolean is_fixed_point, short scale
  )
{
  /* Reference ownership note:
   * This function is passed ownership of py_raw.  In the non-fixed point case,
   * we return that reference.  For fixed point, we transfer ownership of the
   * py_raw reference to the containing tuple, then return our owned reference
   * to the tuple. */
  assert (py_raw != NULL);
  if (!is_fixed_point) {
    /* Simply return the integer value. */
    return py_raw;
  } else {
    /* We're converting a fixed-point rather than an integer; return a 2-tuple
     * of the form: (value, scale) */
    PyObject *fixed_tuple;
    PyObject *py_scale;

    fixed_tuple = PyTuple_New(2);
    if (fixed_tuple == NULL) { return NULL; }

    py_scale = PyInt_FromLong(scale);
    if (py_scale == NULL) {
      Py_DECREF(fixed_tuple);
      return NULL;
    }
    PyTuple_SET_ITEM(fixed_tuple, 0, py_raw); /* "steals" ref to py_raw */
    PyTuple_SET_ITEM(fixed_tuple, 1, py_scale); /* "steals" ref to py_scale */

    return fixed_tuple;
  }
} /* _conv_out_integer_types */


static PyObject *conv_out_short_long(
    char *data,
    short data_type, boolean is_fixed_point, short scale
  )
{
  /* 2004.04.16:64BC: On x86_64/1.5.1pre1, ISC_LONG is actually an int. */
  long conv_long = (long)(
      data_type == SQL_SHORT ?
        ( *((ISC_SHORT *) data) )
      : ( *((ISC_LONG *)  data) )
    );
  PyObject *py_int = PyInt_FromLong(conv_long);
  if (py_int == NULL) { return NULL; }

  return _conv_out_integer_types(py_int, is_fixed_point, scale);
} /* conv_out_short_long */


#ifdef INTERBASE_6_OR_LATER
static PyObject *conv_out_int64(
    char *data,
    boolean is_fixed_point, short scale
  )
{
  const LONG_LONG dataLL = *((LONG_LONG *) data);
  PyObject *py_int = PythonIntOrLongFrom64BitValue(dataLL);
  if (py_int == NULL) { return NULL; }

  return _conv_out_integer_types(py_int, is_fixed_point, scale);
} /* conv_out_int64 */
#endif /* INTERBASE_6_OR_LATER */


static PyObject *conv_out_floating(const double raw,
    const unsigned short dialect, const short scale
  )
{
  /* It's possible that a user would define a field as DECIMAL/NUMERIC with
   * a scale of zero, but the API provides no way for us to detect that. */
  if (dialect >= 3 || scale == 0) {
    return PyFloat_FromDouble(raw);
  }{

  /* The value being converted is from a field that's logically fixed-point
   * rather than floating-point.  This can only arise in pre-dialect-3
   * databases. */

  /* This is a perfect application for Py_BuildValue, but it doesn't
   * support 64-bit integers. */
  PyObject *fixed_tuple;
  PyObject *py_scaled_integer;
  PyObject *py_scale;

  fixed_tuple = PyTuple_New(2);
  if (fixed_tuple != NULL) {
    py_scaled_integer = PythonIntOrLongFrom64BitValue(
        (LONG_LONG) (raw * pow(10.0f, (double) -scale))
      );
    if (py_scaled_integer == NULL) {
      Py_DECREF(fixed_tuple);
      fixed_tuple = NULL;
    } else {
      py_scale = PyInt_FromLong(scale);
      if (py_scale == NULL) {
        Py_DECREF(fixed_tuple);
        fixed_tuple = NULL;
        Py_DECREF(py_scaled_integer);
      } else {
        PyTuple_SET_ITEM(fixed_tuple, 0, py_scaled_integer); /* "steals" ref */
        PyTuple_SET_ITEM(fixed_tuple, 1, py_scale); /* "steals" ref */
      }
    }
  }

  return fixed_tuple;
}} /* conv_out_floating */


/* Date/time types: */

static PyObject *conv_out_timestamp(char *data) {
  struct tm c_tm;
  int ported_ints[2]; /* sizeof(ported_ints) == sizeof(ISC_TIMESTAMP) */
  int microseconds;

  ENTER_GDAL
  ported_ints[0] = isc_vax_integer(data,               sizeof(int));
  ported_ints[1] = isc_vax_integer(data + sizeof(int), sizeof(int));
  isc_decode_timestamp((ISC_TIMESTAMP *) &ported_ints, &c_tm);
  microseconds = MICROSECONDS_FROM_ISC_TIMESTAMP_PTR((ISC_TIMESTAMP *) data);
  LEAVE_GDAL

  return Py_BuildValue("(iiiiiii)",
      c_tm.tm_year+1900, c_tm.tm_mon+1, c_tm.tm_mday,
      c_tm.tm_hour, c_tm.tm_min, c_tm.tm_sec,
      microseconds
    );
} /* conv_out_timestamp */


#ifdef INTERBASE_6_OR_LATER

static PyObject *conv_out_date(char *data) {
  struct tm c_tm;
  int data_as_portable_int;

  ENTER_GDAL
  data_as_portable_int = isc_vax_integer(data, sizeof(int));
  isc_decode_sql_date((ISC_DATE *) &data_as_portable_int, &c_tm);
  LEAVE_GDAL

  return Py_BuildValue("(iii)", c_tm.tm_year+1900, c_tm.tm_mon+1, c_tm.tm_mday);
} /* conv_out_date */


static PyObject *conv_out_time(char *data) {
  struct tm c_tm;
  int data_as_portable_int;
  int microseconds;

  ENTER_GDAL
  data_as_portable_int = isc_vax_integer(data, sizeof(int));
  isc_decode_sql_time((ISC_TIME *) &data_as_portable_int, &c_tm);
  microseconds = MICROSECONDS_FROM_ISC_TIME(*((ISC_TIME *) data));
  LEAVE_GDAL

  return Py_BuildValue("(iiii)",
      c_tm.tm_hour, c_tm.tm_min, c_tm.tm_sec, microseconds
    );
} /* conv_out_time */

#endif /* INTERBASE_6_OR_LATER */

#define conv_out_boolean(data) \
  PyBool_FromLong( *((ISC_BOOLEAN *) (data)) )
