/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "grpc/_adapter/_c/types.h"

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <grpc/grpc.h>
#include <grpc/support/alloc.h>


PyMethodDef pygrpc_Call_methods[] = {
    {"start_batch", (PyCFunction)pygrpc_Call_start_batch, METH_KEYWORDS, ""},
    {"cancel", (PyCFunction)pygrpc_Call_cancel, METH_KEYWORDS, ""},
    {NULL}
};
const char pygrpc_Call_doc[] = "See grpc._adapter._types.Call.";
PyTypeObject pygrpc_Call_type = {
    PyObject_HEAD_INIT(NULL)
    0,                                        /* ob_size */
    "Call",                                   /* tp_name */
    sizeof(Call),                             /* tp_basicsize */
    0,                                        /* tp_itemsize */
    (destructor)pygrpc_Call_dealloc,          /* tp_dealloc */
    0,                                        /* tp_print */
    0,                                        /* tp_getattr */
    0,                                        /* tp_setattr */
    0,                                        /* tp_compare */
    0,                                        /* tp_repr */
    0,                                        /* tp_as_number */
    0,                                        /* tp_as_sequence */
    0,                                        /* tp_as_mapping */
    0,                                        /* tp_hash */
    0,                                        /* tp_call */
    0,                                        /* tp_str */
    0,                                        /* tp_getattro */
    0,                                        /* tp_setattro */
    0,                                        /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    pygrpc_Call_doc,                          /* tp_doc */
    0,                                        /* tp_traverse */
    0,                                        /* tp_clear */
    0,                                        /* tp_richcompare */
    0,                                        /* tp_weaklistoffset */
    0,                                        /* tp_iter */
    0,                                        /* tp_iternext */
    pygrpc_Call_methods,                      /* tp_methods */
    0,                                        /* tp_members */
    0,                                        /* tp_getset */
    0,                                        /* tp_base */
    0,                                        /* tp_dict */
    0,                                        /* tp_descr_get */
    0,                                        /* tp_descr_set */
    0,                                        /* tp_dictoffset */
    0,                                        /* tp_init */
    0,                                        /* tp_alloc */
    0                                         /* tp_new */
};

Call *pygrpc_Call_new_empty(CompletionQueue *cq) {
  Call *call = (Call *)pygrpc_Call_type.tp_alloc(&pygrpc_Call_type, 0);
  call->c_call = NULL;
  call->cq = cq;
  Py_XINCREF(call->cq);
  return call;
}
void pygrpc_Call_dealloc(Call *self) {
  if (self->c_call) {
    grpc_call_destroy(self->c_call);
  }
  Py_XDECREF(self->cq);
  self->ob_type->tp_free((PyObject *)self);
}
PyObject *pygrpc_Call_start_batch(Call *self, PyObject *args, PyObject *kwargs) {
  PyObject *op_list;
  PyObject *user_tag;
  grpc_op *ops;
  size_t nops;
  size_t i;
  size_t j;
  pygrpc_tag *tag;
  grpc_call_error errcode;
  static char *keywords[] = {"ops", "tag", NULL};
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO:start_batch", keywords,
                                   &op_list, &user_tag)) {
    return NULL;
  }
  if (!PyList_Check(op_list)) {
    PyErr_SetString(PyExc_TypeError, "expected a list of OpArgs");
    return NULL;
  }
  nops = PyList_Size(op_list);
  ops = gpr_malloc(sizeof(grpc_op) * nops);
  for (i = 0; i < nops; ++i) {
    PyObject *item = PyList_GET_ITEM(op_list, i);
    if (!pygrpc_produce_op(item, &ops[i])) {
      for (j = 0; j < i; ++j) {
        pygrpc_discard_op(ops[j]);
      }
      return NULL;
    }
  }
  tag = pygrpc_produce_batch_tag(user_tag, self, ops, nops);
  errcode = grpc_call_start_batch(self->c_call, tag->ops, tag->nops, tag);
  gpr_free(ops);
  return PyInt_FromLong(errcode);
}
PyObject *pygrpc_Call_cancel(Call *self, PyObject *args, PyObject *kwargs) {
  PyObject *py_code = NULL;
  grpc_call_error errcode;
  int code;
  char *details = NULL;
  static char *keywords[] = {"code", "details", NULL};
  if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Os:start_batch", keywords,
                                   &py_code, &details)) {
    return NULL;
  }
  if (py_code != NULL && details != NULL) {
    if (!PyInt_Check(py_code)) {
      PyErr_SetString(PyExc_TypeError, "expected integer code");
      return NULL;
    }
    code = PyInt_AsLong(py_code);
    errcode = grpc_call_cancel_with_status(self->c_call, code, details);
  } else if (py_code != NULL || details != NULL) {
    PyErr_SetString(PyExc_ValueError,
                    "if `code` is specified, so must `details`");
    return NULL;
  } else {
    errcode = grpc_call_cancel(self->c_call);
  }
  return PyInt_FromLong(errcode);
}
