%module python_annotations_typing

%include <std_string.i>
%include <std_wstring.i>
%include <std_complex.i>

// Tests the typing annotations
%feature("python:annotations", "typing");

// The default library 'pytyping' typemaps annotate wrapped C/C++ types as typing.Any.
// Opt this test module in to proxy-name annotations by defining the SWIGTYPE 'pytyping'
// typemaps explicitly, which exercises the $pytypename and $&pytypename special variables.
%typemap(pytyping) SWIGTYPE      "$&pytypename";
%typemap(pytyping) SWIGTYPE []   "typing.Optional[$pytypename]";
%typemap(pytyping) SWIGTYPE *    "typing.Optional[$pytypename]";
%typemap(pytyping) SWIGTYPE &    "$pytypename";
%typemap(pytyping) SWIGTYPE &&   "$pytypename";
%typemap(pytyping) enum SWIGTYPE "int";

// Exercise $*pytypename, which removes one pointer level: for a MyStruct ** argument it
// yields the MyStruct proxy name.
%typemap(pytyping) MyStruct ** "typing.Optional[$*pytypename]";

%feature("python:annotations", "0") no_annotations;

%typemap(pytyping) OptionalInt "typing.Optional[int]";
%typemap(pytyping, out = "$typemap(pytyping, short)") MyType "typing.Union[int, float]";

%typemap(in) OptionalInt {
  $1 = $input == Py_None ? OptionalInt() : OptionalInt(PyLong_AsLong($input));
}
%typemap(out) OptionalInt {
  $result = $1.has_value ? PyLong_FromLong($1.value) : Py_None;
}

// Test that a changed type for MyEnum will be picked up correctly.
%typemap(pytyping) MyEnum "bool";
%rename(MyNamespaced2) MyNamespace::Inner::MyNamespaced1;

// Test a typemap using $pytypename for an enum - it should expand to "int" as Python wraps enums as ints.
%typemap(pytyping) PytypenameEnum "typing.Optional[$pytypename]";

%inline %{
namespace Space {
template<class T>
struct Template {
  void mymethod(int, Template* tt) {}
};
}
template<typename T>
Space::Template<T> makeT(int x) {
  return Space::Template<T>();
}
int *global_ints(int &ri, Space::Template<short> t) { return &ri; }
int *global_overloaded(int &ri) { return &ri; }
int *global_overloaded() { return NULL; }
int *no_annotations(int &ri, const char *c) { return NULL; }
%}
%template(TemplateShort) Space::Template<short>;
%template(MakeShort) makeT<short>;

%inline %{
#ifdef SWIGPYTHON_BUILTIN
int is_python_builtin() { return 1; }
#else
int is_python_builtin() { return 0; }
#endif

#if defined SWIGPYTHON_FASTPROXY
int is_python_fastproxy() { return 1; }
#else
int is_python_fastproxy() { return 0; }
#endif
%}

%typemap(in) (int argc, char **argv) {
  /* Check if is a list */
  if (PyList_Check($input)) {
    int i;
    $1 = (int)PyList_Size($input);
    $2 = (char **)malloc(($1+1)*sizeof(char *));
    for (i = 0; i < $1; i++) {
      PyObject *o = PyList_GetItem($input, i);
      if (PyBytes_Check(o)) {
        $2[i] = PyBytes_AsString(PyList_GetItem($input, i));
      } else {
        PyErr_SetString(PyExc_TypeError, "list must contain strings");
        SWIG_fail;
      }
    }
    $2[i] = 0;
  } else {
    PyErr_SetString(PyExc_TypeError, "not a list");
    SWIG_fail;
  }
}

%typemap(freearg) (int argc, char **argv) {
  free((char *) $2);
}

%typemap(pytyping) (int argc, char **argv) "typing.List[str]"

%typemap(in, numinputs=0) short *OutAppend (short temp) { $1 = &temp; }
%typemap(argout) (short *OutAppend) {
  $result = SWIG_AppendOutput($result, PyLong_FromLong(*$1));
}
%typemap(pytyping) short *OutAppend "int"
%apply short *OutAppend { short *OutAppend2 };

%typemap(in, numinputs=0) (short **short_list)(short*temp) { $1 = &temp; }
%typemap(in, numinputs=0) (size_t *short_list_len)(size_t temp) { $1 = &temp; }
%typemap(freearg) (short **short_list) { free(*$1); }
%typemap(argout) (short **short_list, size_t *short_list_len) {
  PyObject *list = PyList_New(*$2);
  for (size_t i = 0; i < *$2; i++)
    PyList_SetItem(list, i, PyLong_FromLong((*$1)[i]));
  $result = SWIG_AppendOutput($result, list);
}
%typemap(pytyping) (short **short_list, size_t *short_list_len) "typing.List[int]"

%inline %{
#include <cstddef>

void take_argv(int argc, char **argv) {}

void take_argv_surround(double before, int argc, char **argv, short after) {}

void argcheck_bool(
  bool a_bool,
  const bool &a_bool_cref
) {}

void argcheck_char(
  char a_char,
  wchar_t a_wchar,
  const char &a_char_cref
) {}

void argcheck_int(
  signed char a_schar,
  unsigned char a_uchar,
  short a_short,
  unsigned short a_ushort,
  int a_int,
  unsigned int a_uint,
  long a_long,
  unsigned long a_ulong,
  long long a_llong,
  unsigned long long a_ullong,
  size_t a_size,
  std::size_t a_stdsize,
  ptrdiff_t a_ptrdiff,
  std::ptrdiff_t a_stdptrdiff,
  const short &a_short_cref,
  const int &a_int_cref,
  const size_t &a_size_cref,
  const std::size_t &a_stdsize_cref,
  const ptrdiff_t &a_ptrdiff_cref,
  const std::ptrdiff_t &a_stdptrdiff_cref
) {}

void argcheck_float(
  float a_float,
  double a_double,
  const double &a_double_cref
) {}

/* long double has no in/out typemaps of its own, so it is wrapped as a pointer to an
   opaque type and a Python float is not accepted. Both forms must stay typing.Any. */
void argcheck_long_double(
  long double a_ldouble,
  const long double &a_ldouble_cref
) {}

void argcheck_complex(
  std::complex<float> a_cfloat,
  std::complex<double> a_cdouble,
  const std::complex<double> &a_cdouble_cref
) {}

void argcheck_str(
  const char* a_cstr,
  const wchar_t *a_wcstr,
  std::string a_stdstr,
  std::wstring a_stdwstr,
  const std::string &a_stdstr_cref
) {}

void argcheck_fnptr(int(*f)(char, bool)) {}
void argcheck_array(float arr[3]) {}

struct OptionalInt {
  OptionalInt() : has_value(false) {}
  OptionalInt(int i) : has_value(true), value(i) {}
  bool has_value;
  int value;
};

OptionalInt optional_square(OptionalInt i) {
  if (i.has_value)
    return OptionalInt(i.value * i.value);
  return OptionalInt();
}

struct MyType {};
MyType docs_do_something_out_type(MyType t) { return MyType(); }

struct MyStruct {
  void do_something(MyStruct& ref, MyStruct* ptr, const MyStruct& cref) {}
};

typedef int MyTypedef;
typedef MyStruct MyStructTypedef;
void use_typedefs(int i, MyTypedef mt, const MyStructTypedef &cref_mst) {}

void use_memberfn_ptr(void (MyStruct::* ptr)(MyStruct&, MyStruct*, const MyStruct&)) {}

void use_member_ptr(int OptionalInt::*ptr) {}

void use_deref(MyStruct **pp) {}

enum MyEnum {
  MyEnumMember1,
  MyEnumMember2,
  MyEnumMember3,
};
typedef MyEnum MyEnumTypedef;

enum MyOtherEnum {
  MyOtherEnumMember1,
  MyOtherEnumMember2,
};
typedef MyOtherEnum MyOtherEnumTypedef;

void use_enums(MyEnum me, MyEnumTypedef met, MyOtherEnum moe, MyOtherEnumTypedef moet) {}

enum PytypenameEnum {
  PytypenameEnumMember1,
};
void use_pytypename_enum(PytypenameEnum e) {}

namespace MyNamespace {
  struct MyNamespaced1 {
    int foo;
  };
  namespace Inner {
    struct MyInner {
      int bar;
    };
    struct MyNamespaced1 {
      int baz;
    };
  }
}

void use_namespaced(MyNamespace::MyNamespaced1 ns1, const MyNamespace::Inner::MyInner &inner1, MyNamespace::Inner::MyNamespaced1 *inner_ns1) {}

void *wrap_ptr(size_t val) { return (void *)val; }
size_t unwrap_ptr(void *ptr) { return (size_t)ptr; }

short &make_short_ref() {
  static short v = 1;
  return v;
}

const short &make_short_cref() {
  static short v = 1;
  return v;
}

MyStruct &make_struct_ref() {
  static MyStruct v;
  return v;
}

const MyStruct &make_struct_cref() {
  static MyStruct v;
  return v;
}

struct HasClassMembers {
  MyStruct member_value;
  MyStruct *member_pointer;
};

// Forward-declared only: no proxy class, so $pytypename falls back to an opaque
// SWIGTYPE_ type wrapper class.
struct ForwardOnly;
void use_forward_only(ForwardOnly *fp) { (void)fp; }

void argoutVoidSingleAppend(bool arg, short *OutAppend) { *OutAppend = 42; }

bool argoutBoolSingleAppend(bool arg, short *OutAppend) {
  *OutAppend = 42;
  return arg;
}

void argoutVoidAppendTwice(bool arg, short *OutAppend, short *OutAppend2) {
  *OutAppend = 42;
  *OutAppend2 = 43;
}

bool argoutBoolAppendTwice(bool arg, short *OutAppend, short *OutAppend2) {
  *OutAppend = 42;
  *OutAppend2 = 43;
  return arg;
}

void argoutMultiarg(short **short_list, size_t *short_list_len) {
  *short_list = NULL;
  *short_list_len = 0;
}

bool argoutBoolMultiarg(bool arg, short **short_list, size_t *short_list_len) {
  *short_list = NULL;
  *short_list_len = 0;
  return arg;
}

void argoutMultiargAfterFirst(int first, short **short_list, size_t *short_list_len) {
  (void)first;
  *short_list = NULL;
  *short_list_len = 0;
}

bool argoutBoolMultiargAfterFirst(int first, short **short_list, size_t *short_list_len) {
  *short_list = NULL;
  *short_list_len = 0;
  return first != 0;
}

void argoutMultiargBetweenFirstLast(int first, short **short_list, size_t *short_list_len, double last) {
  (void)first;
  (void)last;
  *short_list = NULL;
  *short_list_len = 0;
}

bool argoutBoolMultiargBetweenFirstLast(int first, short **short_list, size_t *short_list_len, double last) {
  *short_list = NULL;
  *short_list_len = 0;
  return first != 0 && last != 0.0;
}
%}

// A class-typed %constant is annotated at module level.
%constant MyStruct *CONST_STRUCT = 0;
