/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkWrapPythonMethodDef.c

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkWrapPythonMethodDef.h"
#include "vtkWrapPythonClass.h"
#include "vtkWrapPythonMethod.h"
#include "vtkWrapPythonOverload.h"

#include "vtkParseExtras.h"
#include "vtkWrap.h"
#include "vtkWrapText.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* -------------------------------------------------------------------- */
/* prototypes for the methods used by the python wrappers */

/* output the MethodDef table for this class */
static void vtkWrapPython_ClassMethodDef(
  FILE *fp, const char *classname, ClassInfo *data,
  FunctionInfo **wrappedFunctions, int numberOfWrappedFunctions, int fnum);

/* print out any custom methods */
static void vtkWrapPython_CustomMethods(
  FILE *fp, const char *classname, ClassInfo *data, int do_constructors);

/* -------------------------------------------------------------------- */
/* prototypes for utility methods */

/* check for wrappability, flags may be VTK_WRAP_ARG or VTK_WRAP_RETURN */
static int vtkWrapPython_IsValueWrappable(
  ClassInfo *data, ValueInfo *val, HierarchyInfo *hinfo, int flags);

/* weed out methods that will never be called */
static void vtkWrapPython_RemovePrecededMethods(
  FunctionInfo *wrappedFunctions[],
  int numberWrapped, int fnum);


/* -------------------------------------------------------------------- */
/* Check for type precedence. Some method signatures will just never
 * be called because of the way python types map to C++ types.  If
 * we don't remove such methods, they can lead to ambiguities later.
 *
 * The precedence rule is the following:
 * The type closest to the native Python type wins.
 */

static void vtkWrapPython_RemovePrecededMethods(
  FunctionInfo *wrappedFunctions[],
  int numberOfWrappedFunctions, int fnum)
{
  FunctionInfo *theFunc = wrappedFunctions[fnum];
  const char *name = theFunc->Name;
  FunctionInfo *sig1;
  FunctionInfo *sig2;
  ValueInfo *val1;
  ValueInfo *val2;
  int dim1, dim2;
  int vote1 = 0;
  int vote2 = 0;
  int occ1, occ2;
  unsigned int baseType1, baseType2;
  unsigned int unsigned1, unsigned2;
  unsigned int indirect1, indirect2;
  int i, nargs1, nargs2;
  int argmatch, allmatch;

  if (!name)
  {
    return;
  }

  for (occ1 = fnum; occ1 < numberOfWrappedFunctions; occ1++)
  {
    sig1 = wrappedFunctions[occ1];
    nargs1 = vtkWrap_CountWrappedParameters(sig1);

    if (sig1->Name && strcmp(sig1->Name, name) == 0)
    {
      for (occ2 = occ1+1; occ2 < numberOfWrappedFunctions; occ2++)
      {
        sig2 = wrappedFunctions[occ2];
        nargs2 = vtkWrap_CountWrappedParameters(sig2);
        vote1 = 0;
        vote2 = 0;

        if (nargs2 == nargs1 &&
            sig2->Name && strcmp(sig2->Name, name) == 0)
        {
          allmatch = 1;
          for (i = 0; i < nargs1; i++)
          {
            argmatch = 0;
            val1 = sig1->Parameters[i];
            val2 = sig2->Parameters[i];
            dim1 = (val1->NumberOfDimensions > 0 ? val1->NumberOfDimensions :
                    (vtkWrap_IsPODPointer(val1) || vtkWrap_IsArray(val1)));
            dim2 = (val2->NumberOfDimensions > 0 ? val2->NumberOfDimensions :
                    (vtkWrap_IsPODPointer(val2) || vtkWrap_IsArray(val2)));
            if (dim1 != dim2)
            {
              vote1 = 0;
              vote2 = 0;
              allmatch = 0;
              break;
            }
            else
            {
              baseType1 = (val1->Type & VTK_PARSE_BASE_TYPE);
              baseType2 = (val2->Type & VTK_PARSE_BASE_TYPE);

              unsigned1 = (baseType1 & VTK_PARSE_UNSIGNED);
              unsigned2 = (baseType2 & VTK_PARSE_UNSIGNED);

              baseType1 = (baseType1 & ~VTK_PARSE_UNSIGNED);
              baseType2 = (baseType2 & ~VTK_PARSE_UNSIGNED);

              indirect1 = (val1->Type & VTK_PARSE_INDIRECT);
              indirect2 = (val2->Type & VTK_PARSE_INDIRECT);

              if ((indirect1 == indirect2) &&
                  (unsigned1 == unsigned2) &&
                  (baseType1 == baseType2) &&
                  ((val1->Type & VTK_PARSE_CONST) ==
                   (val2->Type & VTK_PARSE_CONST)))
              {
                argmatch = 1;
              }
              /* double precedes float */
              else if ((indirect1 == indirect2) &&
                  (baseType1 == VTK_PARSE_DOUBLE) &&
                  (baseType2 == VTK_PARSE_FLOAT))
              {
                if (!vote2) { vote1 = 1; }
              }
              else if ((indirect1 == indirect2) &&
                  (baseType1 == VTK_PARSE_FLOAT) &&
                  (baseType2 == VTK_PARSE_DOUBLE))
              {
                if (!vote1) { vote2 = 1; }
              }
              /* unsigned char precedes signed char */
              else if ((indirect1 == indirect2) &&
                       ((baseType1 == VTK_PARSE_CHAR) && unsigned1) &&
                       (baseType2 == VTK_PARSE_SIGNED_CHAR))
              {
                if (!vote2) { vote1 = 1; }
              }
              else if ((indirect1 == indirect2) &&
                       (baseType1 == VTK_PARSE_SIGNED_CHAR) &&
                       ((baseType2 == VTK_PARSE_CHAR) && unsigned2))
              {
                if (!vote1) { vote2 = 1; }
              }
              /* signed precedes unsigned for everything but char */
              else if ((indirect1 == indirect2) &&
                       (baseType1 != VTK_PARSE_CHAR) &&
                       (baseType2 != VTK_PARSE_CHAR) &&
                       (baseType1 == baseType2) &&
                       (unsigned1 != unsigned2))
              {
                if (unsigned2 && !vote2)
                {
                  vote1 = 1;
                }
                else if (unsigned1 && !vote1)
                {
                  vote2 = 1;
                }
              }
              /* integer promotion precedence */
              else if ((indirect1 == indirect2) &&
                       (baseType1 == VTK_PARSE_INT) &&
                       ((baseType2 == VTK_PARSE_SHORT) ||
                        (baseType2 == VTK_PARSE_SIGNED_CHAR) ||
                        ((baseType2 == VTK_PARSE_CHAR) && unsigned2)))
              {
                if (!vote2) { vote1 = 1; }
              }
              else if ((indirect1 == indirect2) &&
                       (baseType2 == VTK_PARSE_INT) &&
                       ((baseType1 == VTK_PARSE_SHORT) ||
                        (baseType1 == VTK_PARSE_SIGNED_CHAR) ||
                        ((baseType1 == VTK_PARSE_CHAR) && unsigned1)))
              {
                if (!vote1) { vote2 = 1; }
              }
              /* a string method precedes a "char *" method */
              else if ((baseType2 == VTK_PARSE_CHAR) &&
                       (indirect2 == VTK_PARSE_POINTER) &&
                       (baseType1 == VTK_PARSE_STRING) &&
                       ((indirect1 == VTK_PARSE_REF) || (indirect1 == 0)))
              {
                if (!vote2) { vote1 = 1; }
              }
              else if ((baseType1 == VTK_PARSE_CHAR) &&
                       (indirect1 == VTK_PARSE_POINTER) &&
                       (baseType2 == VTK_PARSE_STRING) &&
                       ((indirect2 == VTK_PARSE_REF) || (indirect2 == 0)))
              {
                if (!vote1) { vote2 = 1; }
              }
              /* mismatch: both methods are allowed to live */
              else if ((baseType1 != baseType2) ||
                       (unsigned1 != unsigned2) ||
                       (indirect1 != indirect2))
              {
                vote1 = 0;
                vote2 = 0;
                allmatch = 0;
                break;
              }
            }

            if (argmatch == 0)
            {
              allmatch = 0;
            }
          }

          /* if all args match, prefer the non-const method */
          if (allmatch)
          {
            if (sig1->IsConst)
            {
              vote2 = 1;
            }
            else if (sig2->IsConst)
            {
              vote1 = 1;
            }
          }
        }

        if (vote1)
        {
          sig2->Name = 0;
        }
        else if (vote2)
        {
          sig1->Name = 0;
          break;
        }

      } /* for (occ2 ... */
    } /* if (sig1->Name ... */
  } /* for (occ1 ... */
}


/* -------------------------------------------------------------------- */
/* Print out all the python methods that call the C++ class methods.
 * After they're all printed, a Py_MethodDef array that has function
 * pointers and documentation for each method is printed.  In other
 * words, this poorly named function is "the big one". */

void vtkWrapPython_GenerateMethods(
  FILE *fp, const char *classname, ClassInfo *data,
  FileInfo *finfo, HierarchyInfo *hinfo,
  int is_vtkobject, int do_constructors)
{
  int i;
  int fnum;
  int numberOfWrappedFunctions = 0;
  FunctionInfo **wrappedFunctions;
  FunctionInfo *theFunc;
  char *cp;
  const char *ccp;

  wrappedFunctions = (FunctionInfo **)malloc(
    data->NumberOfFunctions*sizeof(FunctionInfo *));

  /* output any custom methods */
  vtkWrapPython_CustomMethods(fp, classname, data, do_constructors);

  /* modify the arg count for vtkDataArray methods */
  vtkWrap_FindCountHints(data, finfo, hinfo);

  /* identify methods that create new instances of objects */
  vtkWrap_FindNewInstanceMethods(data, hinfo);

  /* go through all functions and see which are wrappable */
  for (i = 0; i < data->NumberOfFunctions; i++)
  {
    theFunc = data->Functions[i];

    /* check for wrappability */
    if (vtkWrapPython_MethodCheck(data, theFunc, hinfo) &&
        !theFunc->IsOperator &&
        !theFunc->Template &&
        !vtkWrap_IsDestructor(data, theFunc) &&
        (!vtkWrap_IsConstructor(data, theFunc) == !do_constructors))
    {
      ccp = vtkWrapText_PythonSignature(theFunc);
      cp = (char *)malloc(strlen(ccp)+1);
      strcpy(cp, ccp);
      theFunc->Signature = cp;
      wrappedFunctions[numberOfWrappedFunctions++] = theFunc;
    }
  }

  /* write out the wrapper for each function in the array */
  for (fnum = 0; fnum < numberOfWrappedFunctions; fnum++)
  {
    theFunc = wrappedFunctions[fnum];

    /* check for type precedence, don't need a "float" method if a
       "double" method exists */
    vtkWrapPython_RemovePrecededMethods(
      wrappedFunctions, numberOfWrappedFunctions, fnum);

    /* if theFunc wasn't removed, process all its signatures */
    if (theFunc->Name)
    {
      fprintf(fp,"\n");

      vtkWrapPython_GenerateOneMethod(
        fp, classname, data, hinfo,
        wrappedFunctions, numberOfWrappedFunctions,
        fnum, is_vtkobject, do_constructors);

    } /* is this method non NULL */
  } /* loop over all methods */

  /* the method table for constructors is produced elsewhere */
  if (!do_constructors)
  {
    vtkWrapPython_ClassMethodDef(fp, classname, data, wrappedFunctions,
                                 numberOfWrappedFunctions, fnum);
  }

  free(wrappedFunctions);
}

/* -------------------------------------------------------------------- */
/* output the MethodDef table for this class */
static void vtkWrapPython_ClassMethodDef(
  FILE *fp, const char *classname, ClassInfo *data,
  FunctionInfo **wrappedFunctions, int numberOfWrappedFunctions, int fnum)
{
  /* output the method table, with pointers to each function defined above */
  fprintf(fp,
          "static PyMethodDef Py%s_Methods[] = {\n",
          classname);

  for (fnum = 0; fnum < numberOfWrappedFunctions; fnum++)
  {
    if(wrappedFunctions[fnum]->IsLegacy)
    {
      fprintf(fp,
              "#if !defined(VTK_LEGACY_REMOVE)\n");
    }
    if (wrappedFunctions[fnum]->Name)
    {
      /* string literals must be under 2048 chars */
      size_t maxlen = 2040;
      const char *comment;
      const char *signatures;

      /* format the comment nicely to a 66 char width */
      signatures = vtkWrapText_FormatSignature(
        wrappedFunctions[fnum]->Signature, 66, maxlen - 32);
      comment = vtkWrapText_FormatComment(
        wrappedFunctions[fnum]->Comment, 66);
      comment = vtkWrapText_QuoteString(
        comment, maxlen - strlen(signatures));

      fprintf(fp,
              "  {\"%s\", Py%s_%s, METH_VARARGS,\n",
              wrappedFunctions[fnum]->Name, classname,
              wrappedFunctions[fnum]->Name);

      fprintf(fp,
              "   \"%s\\n\\n%s\"},\n",
              signatures, comment);
    }
    if(wrappedFunctions[fnum]->IsLegacy)
    {
      fprintf(fp,
              "#endif\n");
    }
  }

  /* vtkObject needs a special entry for AddObserver */
  if (strcmp("vtkObject", data->Name) == 0)
  {
    fprintf(fp,
            "  {\"AddObserver\",  Py%s_AddObserver, 1,\n"
            "   \"V.AddObserver(int, function) -> int\\nC++: unsigned long AddObserver(const char *event,\\n    vtkCommand *command, float priority=0.0f)\\n\\nAdd an event callback function(vtkObject, int) for an event type.\\nReturns a handle that can be used with RemoveEvent(int).\"},\n",
            classname);

    /* vtkObject needs a special entry for InvokeEvent */
    fprintf(fp,
            "{\"InvokeEvent\", PyvtkObject_InvokeEvent, METH_VARARGS,\n"
              "   \"V.InvokeEvent(int, void) -> int\\nC++: int InvokeEvent(unsigned long event, void *callData)\\nV.InvokeEvent(string, void) -> int\\nC++: int InvokeEvent(const char *event, void *callData)\\nV.InvokeEvent(int) -> int\\nC++: int InvokeEvent(unsigned long event)\\nV.InvokeEvent(string) -> int\\nC++: int InvokeEvent(const char *event)\\n\\nThis method invokes an event and return whether the event was\\naborted or not. If the event was aborted, the return value is 1,\\notherwise it is 0.\"\n},\n");
  }

  /* vtkObjectBase needs GetAddressAsString, UnRegister */
  else if (strcmp("vtkObjectBase", data->Name) == 0)
  {
    fprintf(fp,
            "  {\"GetAddressAsString\",  Py%s_GetAddressAsString, 1,\n"
            "   \"V.GetAddressAsString(string) -> string\\nC++: const char *GetAddressAsString()\\n\\nGet address of C++ object in format 'Addr=%%p' after casting to\\nthe specified type.  You can get the same information from o.__this__.\"},\n",
            classname);
    fprintf(fp,
            "  {\"Register\", Py%s_Register, 1,\n"
            "   \"V.Register(vtkObjectBase)\\nC++: virtual void Register(vtkObjectBase *o)\\n\\nIncrease the reference count by 1.\\n\"},\n"
            "  {\"UnRegister\", Py%s_UnRegister, 1,\n"
            "   \"V.UnRegister(vtkObjectBase)\\nC++: virtual void UnRegister(vtkObjectBase *o)\\n\\nDecrease the reference count (release by another object). This\\nhas the same effect as invoking Delete() (i.e., it reduces the\\nreference count by 1).\\n\"},\n",
            classname, classname);
  }

  /* python expects the method table to end with a "nullptr" entry */
  fprintf(fp,
          "  {nullptr, nullptr, 0, nullptr}\n"
          "};\n"
          "\n");
}

/* -------------------------------------------------------------------- */
/* Check an arg to see if it is wrappable */

static int vtkWrapPython_IsValueWrappable(
  ClassInfo *data, ValueInfo *val, HierarchyInfo *hinfo, int flags)
{
  static unsigned int wrappableTypes[] = {
    VTK_PARSE_VOID, VTK_PARSE_BOOL, VTK_PARSE_FLOAT, VTK_PARSE_DOUBLE,
    VTK_PARSE_CHAR, VTK_PARSE_UNSIGNED_CHAR, VTK_PARSE_SIGNED_CHAR,
    VTK_PARSE_INT, VTK_PARSE_UNSIGNED_INT,
    VTK_PARSE_SHORT, VTK_PARSE_UNSIGNED_SHORT,
    VTK_PARSE_LONG, VTK_PARSE_UNSIGNED_LONG,
    VTK_PARSE_SSIZE_T, VTK_PARSE_SIZE_T,
    VTK_PARSE_UNKNOWN,
    VTK_PARSE_LONG_LONG, VTK_PARSE_UNSIGNED_LONG_LONG,
    VTK_PARSE_OBJECT, VTK_PARSE_QOBJECT, VTK_PARSE_STRING,
#ifndef VTK_PYTHON_NO_UNICODE
    VTK_PARSE_UNICODE_STRING,
#endif
    0
  };

  const char *aClass;
  unsigned int baseType;
  int j;

  if ((flags & VTK_WRAP_RETURN) != 0)
  {
    if (vtkWrap_IsVoid(val))
    {
      return 1;
    }

    if (vtkWrap_IsNArray(val))
    {
      return 0;
    }
  }

  /* wrap std::vector<T> (IsScalar means "not pointer or array") */
  if (vtkWrap_IsStdVector(val) &&
      vtkWrap_IsScalar(val))
  {
    size_t l, n;
    const char *tname;
    const char **args;
    const char *defaults[2] = { NULL, "" };
    int wrappable = 0;
    vtkParse_DecomposeTemplatedType(val->Class, &tname, 2, &args, defaults);
    l = vtkParse_BasicTypeFromString(args[0], &baseType, &aClass, &n);
    /* check that type has no following '*', '[]', or '<>' decorators */
    if (args[0][l] == '\0')
    {
      if (baseType != VTK_PARSE_UNKNOWN &&
          baseType != VTK_PARSE_OBJECT &&
          baseType != VTK_PARSE_QOBJECT &&
          baseType != VTK_PARSE_CHAR)
      {
        for (j = 0; wrappableTypes[j] != 0; j++)
        {
          if (baseType == wrappableTypes[j])
          {
            wrappable = 1;
            break;
          }
        }
      }
    }
    vtkParse_FreeTemplateDecomposition(tname, 2, args);
    return wrappable;
  }

  aClass = val->Class;
  baseType = (val->Type & VTK_PARSE_BASE_TYPE);

  /* go through all types that are indicated as wrappable */
  for (j = 0; wrappableTypes[j] != 0; j++)
  {
    if (baseType == wrappableTypes[j]) { break; }
  }
  if (wrappableTypes[j] == 0)
  {
    return 0;
  }

  if (vtkWrap_IsRef(val) &&
      !vtkWrap_IsScalar(val) &&
      !vtkWrap_IsArray(val) &&
      !vtkWrap_IsPODPointer(val))
  {
    return 0;
  }

  if (vtkWrap_IsScalar(val))
  {
    if (vtkWrap_IsNumeric(val) ||
        val->IsEnum || /* marked as enum in ImportExportEnumTypes */
        vtkWrap_IsEnumMember(data, val) ||
        vtkWrap_IsString(val))
    {
      return 1;
    }
    if (vtkWrap_IsObject(val))
    {
      if (vtkWrap_IsSpecialType(hinfo, aClass) ||
          vtkWrapPython_HasWrappedSuperClass(hinfo, aClass, NULL))
      {
        return 1;
      }
    }
  }
  else if (vtkWrap_IsArray(val) || vtkWrap_IsNArray(val))
  {
    if (vtkWrap_IsNumeric(val))
    {
      return 1;
    }
  }
  else if (vtkWrap_IsPointer(val))
  {
    if (vtkWrap_IsCharPointer(val) ||
        vtkWrap_IsVoidPointer(val) ||
        vtkWrap_IsZeroCopyPointer(val) ||
        vtkWrap_IsPODPointer(val))
    {
      return 1;
    }
    if (vtkWrap_IsPythonObject(val))
    {
      return 1;
    }
    else if (vtkWrap_IsObject(val))
    {
      if (vtkWrap_IsVTKObjectBaseType(hinfo, aClass))
      {
        return 1;
      }
    }
  }

  return 0;
}

/* -------------------------------------------------------------------- */
/* Check a method to see if it is wrappable in python */

int vtkWrapPython_MethodCheck(
  ClassInfo *data, FunctionInfo *currentFunction, HierarchyInfo *hinfo)
{
  int i, n;

  /* some functions will not get wrapped no matter what */
  if (currentFunction->IsExcluded ||
      currentFunction->IsDeleted ||
      currentFunction->Access != VTK_ACCESS_PUBLIC ||
      vtkWrap_IsInheritedMethod(data, currentFunction))
  {
    return 0;
  }

  /* new and delete are meaningless in wrapped languages */
  if (currentFunction->Name == 0 ||
      strcmp("Register", currentFunction->Name) == 0 ||
      strcmp("UnRegister", currentFunction->Name) == 0 ||
      strcmp("Delete", currentFunction->Name) == 0 ||
      strcmp("New", currentFunction->Name) == 0)
  {
    return 0;
  }

  /* function pointer arguments for callbacks */
  if (currentFunction->NumberOfParameters == 2 &&
      vtkWrap_IsVoidFunction(currentFunction->Parameters[0]) &&
      vtkWrap_IsVoidPointer(currentFunction->Parameters[1]) &&
      !vtkWrap_IsConst(currentFunction->Parameters[1]) &&
      vtkWrap_IsVoid(currentFunction->ReturnValue))
  {
    return 1;
  }

  n = vtkWrap_CountWrappedParameters(currentFunction);

  /* check to see if we can handle all the args */
  for (i = 0; i < n; i++)
  {
    if (!vtkWrapPython_IsValueWrappable(
          data, currentFunction->Parameters[i], hinfo, VTK_WRAP_ARG))
    {
      return 0;
    }
  }

  /* check the return value */
  if (!vtkWrapPython_IsValueWrappable(
        data, currentFunction->ReturnValue, hinfo, VTK_WRAP_RETURN))
  {
    return 0;
  }

  return 1;
}


/* -------------------------------------------------------------------- */
/* generate code for custom methods for some classes */
static void vtkWrapPython_CustomMethods(
  FILE *fp, const char *classname, ClassInfo *data, int do_constructors)
{
  int i;
  FunctionInfo *theFunc;

  /* the python vtkObject needs special hooks for observers */
  if (strcmp("vtkObject", data->Name) == 0 &&
      do_constructors == 0)
  {
    /* Remove the original AddObserver method */
    for (i = 0; i < data->NumberOfFunctions; i++)
    {
      theFunc = data->Functions[i];

      if (strcmp(theFunc->Name, "AddObserver") == 0)
      {
        data->Functions[i]->Name = NULL;
      }
    }

    /* Add the AddObserver method to vtkObject. */
    fprintf(fp,
            "static PyObject *\n"
            "Py%s_AddObserver(PyObject *self, PyObject *args)\n"
            "{\n"
            "  vtkPythonArgs ap(self, args, \"AddObserver\");\n"
            "  vtkObjectBase *vp = ap.GetSelfPointer(self, args);\n"
            "  %s *op = static_cast<%s *>(vp);\n"
            "\n"
            "  const char *temp0s = nullptr;\n"
            "  int temp0i = 0;\n"
            "  PyObject *temp1 = nullptr;\n"
            "  float temp2 = 0.0f;\n"
            "  unsigned long tempr;\n"
            "  PyObject *result = nullptr;\n"
            "  int argtype = 0;\n"
            "\n",
            classname, data->Name, data->Name);

    fprintf(fp,
            "  if (op)\n"
            "  {\n"
            "    if (ap.CheckArgCount(2,3) &&\n"
            "        ap.GetValue(temp0i) &&\n"
            "        ap.GetFunction(temp1) &&\n"
            "        (ap.NoArgsLeft() || ap.GetValue(temp2)))\n"
            "    {\n"
            "      argtype = 1;\n"
            "    }\n"
            "  }\n"
            "\n"
            "  if (op && !argtype)\n"
            "  {\n"
            "    PyErr_Clear();\n"
            "    ap.Reset();\n"
            "\n"
            "    if (ap.CheckArgCount(2,3) &&\n"
            "        ap.GetValue(temp0s) &&\n"
            "        ap.GetFunction(temp1) &&\n"
            "        (ap.NoArgsLeft() || ap.GetValue(temp2)))\n"
            "    {\n"
            "      argtype = 2;\n"
            "    }\n"
            "  }\n"
            "\n");

    fprintf(fp,
            "  if (argtype)\n"
            "  {\n"
            "    vtkPythonCommand *cbc = vtkPythonCommand::New();\n"
            "    cbc->SetObject(temp1);\n"
            "    cbc->SetThreadState(PyThreadState_Get());\n"
            "\n"
            "    if (argtype == 1)\n"
            "    {\n"
            "      if (ap.IsBound())\n"
            "      {\n"
            "        tempr = op->AddObserver(temp0i, cbc, temp2);\n"
            "      }\n"
            "      else\n"
            "      {\n"
            "        tempr = op->%s::AddObserver(temp0i, cbc, temp2);\n"
            "      }\n"
            "    }\n"
            "    else\n"
            "    {\n"
            "      if (ap.IsBound())\n"
            "      {\n"
            "        tempr = op->AddObserver(temp0s, cbc, temp2);\n"
            "      }\n"
            "      else\n"
            "      {\n"
            "        tempr = op->%s::AddObserver(temp0s, cbc, temp2);\n"
            "      }\n"
            "    }\n"
            "    PyVTKObject_AddObserver(self, tempr);\n"
            "\n",
            data->Name, data->Name);

    fprintf(fp,
            "    cbc->Delete();\n"
            "\n"
            "    if (!ap.ErrorOccurred())\n"
            "    {\n"
            "      result = ap.BuildValue(tempr);\n"
            "    }\n"
            "  }\n"
            "\n"
            "  return result;\n"
            "}\n"
            "\n");


    /* the python vtkObject needs a special InvokeEvent to turn any
       calldata into an appropriately unwrapped void pointer */

    /* different types of callback data */

    int numCallBackTypes = 5;

    static const char *callBackTypeString[] = {
      "z", "", "i", "d", "V" };

    static const char *fullCallBackTypeString[] = {
      "z", "", "i", "d", "V *vtkObjectBase" };

    static const char *callBackTypeDecl[] = {
      "  const char *calldata = nullptr;\n",
      "",
      "  long calldata;\n",
      "  double calldata;\n",
      "  vtkObjectBase *calldata = nullptr;\n" };

    static const char *callBackReadArg[] = {
      " &&\n      ap.GetValue(calldata)",
      "",
      " &&\n      ap.GetValue(calldata)",
      " &&\n      ap.GetValue(calldata)",
      " &&\n      ap.GetVTKObject(calldata, \"vtkObject\")" };

    static const char *methodCallSecondHalf[] = {
      ", const_cast<char *>(calldata)",
      "",
      ", &calldata",
      ", &calldata",
      ", calldata" };

    /* two ways to refer to an event */
    static const char *eventTypeString[] = { "L", "z" };
    static const char *eventTypeDecl[] = {
      "  unsigned long event;\n",
      "  const char *event = nullptr;\n" };

    int callBackIdx, eventIdx;

    /* Remove the original InvokeEvent method */
    for (i = 0; i < data->NumberOfFunctions; i++)
    {
      theFunc = data->Functions[i];

      if (theFunc->Name && strcmp(theFunc->Name, "InvokeEvent") == 0)
      {
        data->Functions[i]->Name = NULL;
      }
    }

    /* Add the InvokeEvent method to vtkObject. */
    fprintf(fp,
      "// This collection of methods that handle InvokeEvent are\n"
      "// generated by a special case in vtkWrapPythonMethodDef.c\n"
      "// The last characters of the method name indicate the type signature\n"
      "// of the overload they handle: for example, \"_zd\" indicates that\n"
      "// the event type is specified by string and the calldata is a double\n");

    for (callBackIdx = 0; callBackIdx < numCallBackTypes; callBackIdx++)
    {
      for (eventIdx = 0; eventIdx < 2; eventIdx++)
      {
        fprintf(fp,
            "static PyObject *\n"
            "PyvtkObject_InvokeEvent_%s%s(PyObject *self, PyObject *args)\n"
            "{\n"
            "  vtkPythonArgs ap(self, args, \"InvokeEvent\");\n"
            "  vtkObjectBase *vp = ap.GetSelfPointer(self, args);\n"
            "  vtkObject *op = static_cast<vtkObject *>(vp);\n"
            "\n"
            "%s%s"
            "  PyObject *result = nullptr;\n"
            "\n"
            "  if (op && ap.CheckArgCount(%d) &&\n"
            "      ap.GetValue(event)%s)\n"
            "  {\n"
            "    int tempr = op->InvokeEvent(event%s);\n"
            "\n"
            "    if (!ap.ErrorOccurred())\n"
            "    {\n"
            "      result = ap.BuildValue(tempr);\n"
            "    }\n"
            "  }\n"
            "  return result;\n"
            "}\n"
            "\n",
            eventTypeString[eventIdx],
            callBackTypeString[callBackIdx],
            eventTypeDecl[eventIdx],
            callBackTypeDecl[callBackIdx],
            1 + (callBackReadArg[callBackIdx][0] != '\0'),
            callBackReadArg[callBackIdx],
            methodCallSecondHalf[callBackIdx]);
      }
    }
    fprintf(fp,
              "static PyMethodDef PyvtkObject_InvokeEvent_Methods[] = {\n");
    for(callBackIdx = 0; callBackIdx < numCallBackTypes; callBackIdx++)
    {
      for(eventIdx = 0; eventIdx < 2; eventIdx++)
      {
        fprintf(fp,
          "  {nullptr, PyvtkObject_InvokeEvent_%s%s, METH_VARARGS,\n"
          "   \"@%s%s\"},\n",
          eventTypeString[eventIdx],
          callBackTypeString[callBackIdx],
          eventTypeString[eventIdx],
          fullCallBackTypeString[callBackIdx]);
      }
    }

    fprintf(fp,
            "  {nullptr, nullptr, 0, nullptr}\n"
            "};\n"
            "\n"
            "static PyObject *\n"
            "PyvtkObject_InvokeEvent(PyObject *self, PyObject *args)\n"
            "{\n"
            "  PyMethodDef *methods = PyvtkObject_InvokeEvent_Methods;\n"
            "  int nargs = vtkPythonArgs::GetArgCount(self, args);\n"
            "\n"
            "  switch(nargs)\n"
            "  {\n"
            "    case 1:\n"
            "    case 2:\n"
            "      return vtkPythonOverload::CallMethod(methods, self, args);\n"
            "  }\n"
            "\n"
            "  vtkPythonArgs::ArgCountError(nargs, \"InvokeEvent\");\n"
            "  return nullptr;\n"
            "}\n");
  }

  /* the python vtkObjectBase needs a couple extra functions */
  if (strcmp("vtkObjectBase", data->Name) == 0 &&
      do_constructors == 0)
  {
     /* remove the original methods, if they exist */
    for (i = 0; i < data->NumberOfFunctions; i++)
    {
      theFunc = data->Functions[i];

      if ((strcmp(theFunc->Name, "GetAddressAsString") == 0) ||
          (strcmp(theFunc->Name, "Register") == 0) ||
          (strcmp(theFunc->Name, "UnRegister") == 0))
      {
        theFunc->Name = NULL;
      }
    }

    /* add the GetAddressAsString method to vtkObjectBase */
    fprintf(fp,
            "static PyObject *\n"
            "Py%s_GetAddressAsString(PyObject *self, PyObject *args)\n"
            "{\n"
            "  vtkPythonArgs ap(self, args, \"GetAddressAsString\");\n"
            "  vtkObjectBase *vp = ap.GetSelfPointer(self, args);\n"
            "  %s *op = static_cast<%s *>(vp);\n"
            "\n"
            "  const char *temp0;\n"
            "  char tempr[256];\n"
            "  PyObject *result = nullptr;\n"
            "\n"
            "  if (op && ap.CheckArgCount(1) &&\n"
            "      ap.GetValue(temp0))\n"
            "  {\n"
            "    sprintf(tempr, \"Addr=%%p\", static_cast<void*>(op));\n"
            "\n"
            "    result = ap.BuildValue(tempr);\n"
            "  }\n"
            "\n"
            "  return result;\n"
            "}\n"
            "\n",
            classname, data->Name, data->Name);

    /* Override the Register method to check whether to ignore Register */
    fprintf(fp,
            "static PyObject *\n"
            "Py%s_Register(PyObject *self, PyObject *args)\n"
            "{\n"
            "  vtkPythonArgs ap(self, args, \"Register\");\n"
            "  vtkObjectBase *vp = ap.GetSelfPointer(self, args);\n"
            "  %s *op = static_cast<%s *>(vp);\n"
            "\n"
            "  vtkObjectBase *temp0 = nullptr;\n"
            "  PyObject *result = nullptr;\n"
            "\n"
            "  if (op && ap.CheckArgCount(1) &&\n"
            "      ap.GetVTKObject(temp0, \"vtkObjectBase\"))\n"
            "  {\n"
            "    if (!PyVTKObject_Check(self) ||\n"
            "        (PyVTKObject_GetFlags(self) & VTK_PYTHON_IGNORE_UNREGISTER) == 0)\n"
            "    {\n"
            "      if (ap.IsBound())\n"
            "      {\n"
            "        op->Register(temp0);\n"
            "      }\n"
            "      else\n"
            "      {\n"
            "        op->%s::Register(temp0);\n"
            "      }\n"
            "    }\n"
            "\n"
            "    if (!ap.ErrorOccurred())\n"
            "    {\n"
            "      result = ap.BuildNone();\n"
            "    }\n"
            "  }\n"
            "\n"
            "  return result;\n"
            "}\n"
            "\n",
            classname, data->Name, data->Name, data->Name);

    /* Override the UnRegister method to check whether to ignore UnRegister */
    fprintf(fp,
            "static PyObject *\n"
            "Py%s_UnRegister(PyObject *self, PyObject *args)\n"
            "{\n"
            "  vtkPythonArgs ap(self, args, \"UnRegister\");\n"
            "  vtkObjectBase *vp = ap.GetSelfPointer(self, args);\n"
            "  %s *op = static_cast<%s *>(vp);\n"
            "\n"
            "  vtkObjectBase *temp0 = nullptr;\n"
            "  PyObject *result = nullptr;\n"
            "\n"
            "  if (op && ap.CheckArgCount(1) &&\n"
            "      ap.GetVTKObject(temp0, \"vtkObjectBase\"))\n"
            "  {\n"
            "    if (!PyVTKObject_Check(self) ||\n"
            "        (PyVTKObject_GetFlags(self) & VTK_PYTHON_IGNORE_UNREGISTER) == 0)\n"
            "    {\n"
            "      if (ap.IsBound())\n"
            "      {\n"
            "        op->UnRegister(temp0);\n"
            "      }\n"
            "      else\n"
            "      {\n"
            "        op->%s::UnRegister(temp0);\n"
            "      }\n"
            "    }\n"
            "\n"
            "    if (!ap.ErrorOccurred())\n"
            "    {\n"
            "      result = ap.BuildNone();\n"
            "    }\n"
            "  }\n"
            "\n"
            "  return result;\n"
            "}\n"
            "\n",
            classname, data->Name, data->Name, data->Name);
  }

  if (strcmp("vtkCollection", data->Name) == 0 &&
      do_constructors == 0)
  {
    fprintf(fp,
            "static PyObject *\n"
            "PyvtkCollection_Iter(PyObject *self)\n"
            "{\n"
            "  PyVTKObject *vp = (PyVTKObject *)self;\n"
            "  vtkCollection *op = static_cast<vtkCollection *>(vp->vtk_ptr);\n"
            "\n"
            "  PyObject *result = nullptr;\n"
            "\n"
            "  if (op)\n"
            "  {\n"
            "    vtkCollectionIterator *tempr = op->NewIterator();\n"
            "    if (tempr != nullptr)\n"
            "    {\n"
            "      result = vtkPythonArgs::BuildVTKObject(tempr);\n"
            "      PyVTKObject_GetObject(result)->UnRegister(0);\n"
            "    }\n"
            "  }\n"
            "\n"
            "  return result;\n"
            "}\n");
  }

  if (strcmp("vtkCollectionIterator", data->Name) == 0 &&
      do_constructors == 0)
  {
    fprintf(fp,
            "static PyObject *\n"
            "PyvtkCollectionIterator_Next(PyObject *self)\n"
            "{\n"
            "  PyVTKObject *vp = (PyVTKObject *)self;\n"
            "  vtkCollectionIterator *op = static_cast<vtkCollectionIterator*>(vp->vtk_ptr);\n"
            "\n"
            "  PyObject *result = nullptr;\n"
            "\n"
            "  if (op)\n"
            "  {\n"
            "    vtkObject *tempr = op->GetCurrentObject();\n"
            "    op->GoToNextItem();\n"
            "    if (tempr != nullptr)\n"
            "    {\n"
            "      result = vtkPythonArgs::BuildVTKObject(tempr);\n"
            "    }\n"
            "  }\n"
            "\n"
            "  return result;\n"
            "}\n"
            "\n"
            "static PyObject *\n"
            "PyvtkCollectionIterator_Iter(PyObject *self)\n"
            "{\n"
            "  Py_INCREF(self);\n"
            "  return self;\n"
            "}\n");
  }
}
