/* teamtrack-server.cpp -- Python wrapper round TSServer class
   Gareth Rees, Ravenbrook Limited, 2000-08-08
   $Id: //info.ravenbrook.com/project/p4dti/branch/2000-12-07/document-history/code/python-teamtrack-interface/teamtrack-server.cpp#2 $

   See "Python interface to TeamTrack: design" for the design
   <master/design/python-teamtrack-interface/>. */

/* Copyright 2000 Ravenbrook Limited.  This document is provided "as is",
   without any express or implied warranty. In no event will the authors
   be held liable for any damages arising from the use of this document.
   You may make and distribute copies and derivative works of this
   document provided that (1) you do not charge a fee for this document or
   for its distribution, and (2) you retain as they appear all copyright
   and licence notices and document history entries, and (3) you append
   descriptions of your modifications to the document history. */

#include "teamtrack-module.h"
#include "teamtrack-server.h"
#include "teamtrack-record.h"


/* teamtrack_server_new(s, delete_p) returns a Python wrapper for the TSServer
   object s, or NULL if there is an error.  Iff delete_p is true, then the
   TSServer object will be deleted when the Python wrapper is deleted. */

PyObject *
teamtrack_server_new(TSServer *s, int delete_p) {
  teamtrack_server *self =
    PyObject_NEW(teamtrack_server, &teamtrack_server_type);
  if (self == NULL) {
    return NULL;
  }
  self->s = s;
  self->delete_p = delete_p;
  return (PyObject *)self;
}


/* teamtrack_record_list_to_python_list(tts, ts_records) returns a Python list
   containing a teamtrack_record object for each TSRecord object in the
   ts_records argument.  The tts argument is the teamtrack_server object to 
   which the record list belongs. */

static PyObject *
teamtrack_record_list_to_python_list(teamtrack_server *tts, TSRecordList *ts_records) {
  int n_records = ts_records->Length();
  PyObject *py_records = PyList_New(n_records);
  if (py_records == NULL) {
    return NULL;
  }
  int i;			/* Position in Python list */
  TSPosition *p;		/* Position in TSRecordList */
  for (i = 0, p = ts_records->GetFirst();
       i < n_records && p;
       ++i, p = ts_records->GetNext(p)) {
    TSRecord *ts_record = ts_records->GetAt(p);
    if (ts_record == NULL) {
      /* Can't just 'raise' here, since py_records would leak. */
      teamtrack_set_error(tts->s);
      break;
    }
    /* I believe it's safe to pass ownership of the TSRecord objects to Python,
       because the TSRecordList class doesn't actually delete the records in it
       unless you call TSList::EmptyAndDestroyList.  See TSList.C. */
    PyObject *py_record = teamtrack_record_new(ts_record, tts, 1);
    if (py_record == NULL) {
      break;
    }
    PyList_SetItem(py_records, i, py_record);
    /* No need to decref py_record since PyList_SetItem doesn't incref it */
  }
  if (PyErr_Occurred()) {
    Py_DECREF(py_records);	/* Delete py_records. */
    return NULL;
  }
  return py_records;
}


/* Instance methods */

static PyObject *
teamtrack_server_delete_record(PyObject *self, PyObject *args) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  int table_id, record_id;
  if (!PyArg_ParseTuple(args, "ii", &table_id, &record_id)) {
    return NULL;
  }
  int rc = tts->s->DeleteRecord(table_id, record_id);
  teamtrack_try(rc, tts->s, NULL);
  Py_INCREF(Py_None); /* It will be on Python's stack when returned. */
  return Py_None;
}

static PyObject *
teamtrack_server_new_record(PyObject *self, PyObject *args) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  int table_id;
  if (!PyArg_ParseTuple(args, "i", &table_id)) {
    return NULL;
  }
  TSRecord *r = new TSRecord(table_id, tts->s);
  teamtrack_assert(r, NULL);
  return teamtrack_record_new(r, tts, 1);
}

static PyObject *
teamtrack_server_query(PyObject *self, PyObject *args) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  int table_id;
  char *where_clause;
  if (!PyArg_ParseTuple(args, "is", &table_id, &where_clause)) {
    return NULL;
  }
  TSRecordList ts_records;
  int rc = tts->s->ReadRecordListWithWhere(&ts_records, table_id,
					   where_clause);
  teamtrack_try(rc, tts->s, NULL);
  return teamtrack_record_list_to_python_list(tts, &ts_records);
}

static PyObject *
teamtrack_server_read_record(PyObject *self, PyObject *args) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  int table_id, record_id;
  if (!PyArg_ParseTuple(args, "ii", &table_id, &record_id)) {
    return NULL;
  }
  TSRecord *r = new TSRecord(table_id, tts->s);
  teamtrack_assert(r, NULL);
  if (tts->s->ReadRecord(r, record_id) != TS_OK) {
    teamtrack_set_error(tts->s);
    delete r;
    return NULL;
  }
  return teamtrack_record_new(r, tts, 1);
}

static PyObject *
teamtrack_server_read_state_list(PyObject *self, PyObject *args) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  int workflow_id;
  int include_parent = 0;
  if (!PyArg_ParseTuple(args, "i|i", &workflow_id, &include_parent)) {
    return NULL;
  }
  TSRecordList ts_records;
  int rc = tts->s->ReadStateList(&ts_records, workflow_id, include_parent);
  teamtrack_try(rc, tts->s, NULL);
  return teamtrack_record_list_to_python_list(tts, &ts_records);
}

static PyObject *
teamtrack_server_read_transition_list(PyObject *self, PyObject *args) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  int project_id;
  if (!PyArg_ParseTuple(args, "i", &project_id)) {
    return NULL;
  }
  TSRecordList ts_records;
  int rc = tts->s->ReadTransitionList(&ts_records, project_id);
  teamtrack_try(rc, tts->s, NULL);
  return teamtrack_record_list_to_python_list(tts, &ts_records);
}

static struct PyMethodDef teamtrack_server_methods[] = {
  { "delete_record",        teamtrack_server_delete_record,        1 },
  { "new_record",           teamtrack_server_new_record,           1 },
  { "query",                teamtrack_server_query,                1 },
  { "read_record",          teamtrack_server_read_record,          1 },
  { "read_state_list",      teamtrack_server_read_state_list,      1 },
  { "read_transition_list", teamtrack_server_read_transition_list, 1 },
  { NULL, NULL, 0 }		/* End of methods */
};


/* Basic Python operators */

static void
teamtrack_server_dealloc(PyObject *self) {
  if (!teamtrack_server_p(self)) {
    Py_FatalError("teamtrack_server_dealloc called on object that's not "
		  "a teamtrack_server.");
  }
  teamtrack_server *tts = (teamtrack_server *)self;
  if (tts->delete_p) {
    delete tts->s;
  }
  PyMem_DEL(self);
}

static PyObject *
teamtrack_server_repr(PyObject *self) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  teamtrack_server *tts = (teamtrack_server *)self;
  return PyString_FromString("<TeamShare server>");
}

static PyObject *
teamtrack_server_getattr(PyObject *self, char *name) {
  teamtrack_check_type(teamtrack_server, self, NULL);
  return Py_FindMethod(teamtrack_server_methods, self, name);
}


/* Create the teamtrack.server class */

PyTypeObject teamtrack_server_type = {
  /* Type header */
  PyObject_HEAD_INIT(&PyType_Type)
  0,				/* ob_size */
  "server",			/* tp_name */
  sizeof(teamtrack_server),	/* tp_basicsize */
  0,				/* tp_itemsize */

  /* Basic methods */
  teamtrack_server_dealloc,
  NULL,				/* print */
  teamtrack_server_getattr,
  NULL,				/* setatts */
  NULL,				/* compare */
  teamtrack_server_repr,

  /* Type categories */
  NULL,				/* number operators */
  NULL,				/* sequence operators */
  NULL,				/* mapping operators */

  /* Other methods are NULL: see Python's object.h for details */
};


/* B. Document History

   2000-08-08 GDR Created.

   2000-08-29 GDR Changed "tTrack" to "TeamTrack" throughout.

   1976 2000-08-31 GDR Removed obsolete read_case method from the server class.
   Added new_record method to the server class.

   2000-08-31 GDR Added delete_record method to server class.

   2000-09-06 GDR Added add_field method to teamtrack server class.

   2000-09-07 GDR Moved add_field() method implementation from server class to
   record class.

   2000-10-13 GDR Added read_state_list and read_transition_list to the
   teamtrack server object.  The former returns a list of available states in a
   workflow.  The latter returns a list of available transitions in a project.
   It is more maintainable (and more likely to be correct) to use these
   functions to query TeamTrack's workflow model than to try to duplicate
   TeamTrack's logic using only the schema documentation.

*/

