%module special_variable_macros

// test $typemap() special variable function
// these tests are not typical of how $typemap() should be used, but it checks that it is mostly working

%warnfilter(SWIGWARN_GO_NAME_CONFLICT);                       /* Ignoring 'NewName' due to Go name ('NewName') conflict with 'Name' */

%{
#if defined(_MSC_VER)
  #pragma warning(disable: 4996) // 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C++ conformant name: _strdup. See online help for details.
#endif
#include <stdlib.h>
#include <string.h>
%}

%ignore Name::operator=;

%inline %{
struct Name {
  Name(const char *n="none") : name(strdup(n ? n : "")) {}
  Name(const Name& x) : name(strdup(x.name)) {}
  Name& operator= (const Name& x)
  {
    if (this != &x) {
      free(this->name);
      this->name = strdup(x.name);
    }
    return *this;
  }
  ~Name () { free(this->name); }
  const char *getName() const { return name; };
  Name *getNamePtr() { return this; };
private:
  char *name;
};
struct NameWrap {
  NameWrap(const char *n="casternone") : name(n) {}
  Name *getNamePtr() { return &name; };
private:
  Name name;
};

// Global variable for testing whether examplekw was touched
int accessed_examplekw = 0;
%}

// check $1 and $input get expanded properly when used from $typemap()
%typemap(in, examplekw="accessed_examplekw=1;") Name *GENERIC ($*1_type temp)
%{
  /*%typemap(in) Name *GENERIC start */
  temp = Name("$specialname");
  (void)$input;
  $1 = ($1_ltype) &temp;
  /*%typemap(in) Name *GENERIC end */
%}

// This would never be done in real code, it is just a test of what madness can be done.
// Note that the special variable substitutions $*1_type, $descriptor etc are for NameWrap 
// even when used within the Name typemap via $typemap. I can't think of any useful use cases
// for this behaviour in the C/C++ typemaps, but it is possible.
%typemap(in) NameWrap *NAMEWRAP ($*1_type temp)
%{
  /*%typemap(in) NameWrap *NAMEWRAP start */
  temp = $*1_ltype("$descriptor");
  (void)$input;
  $1 = temp.getNamePtr();
  /*%typemap(in) NameWrap *NAMEWRAP end */
%}

// check $descriptor gets expanded properly when used in a fragment
%fragment("nameDescriptor", "header")
%{
/*%fragment("getNameDescriptor", "header") start */
static const char *nameDescriptor = "$descriptor(Name)";
/*%fragment("getNameDescriptor", "header") end */
%}


//////////////////////////////////////////////////////////////////////////////////////

// This should use Name *GENERIC typemap which ignores passed in Name * and instead uses a newly a newly constructed Name
// held in a typemap variable with name="$specialname"
%typemap(in) Name *jack {
// %typemap(in) Name *jack start
$typemap(in, Name *GENERIC)
$typemap(in:examplekw, Name *GENERIC)
// %typemap(in) Name *jack end
}

// as above, but also perform variable substitution
%typemap(in) Name *jill {
// %typemap(in) Name *jill start
$typemap(in, Name *GENERIC, specialname=jilly)
// %typemap(in) Name *jill end
}

%typemap(in) Name *mary {
// %typemap(in) Name *mary start
$typemap(in, NameWrap *NAMEWRAP)
// %typemap(in) Name *mary end
}

%typemap(in, fragment="nameDescriptor") Name *james (Name temp) {
  // %typemap(in) Name *james start
  temp = Name(nameDescriptor);
  (void)$input;
  $1 = &temp;
  // %typemap(in) Name *james end
}

%inline %{
const char * testFred(Name *fred) {
  return fred->getName();
}
const char * testJack(Name *jack) {
  return jack->getName();
}
const char * testJill(Name *jill) {
  return jill->getName();
}
const char * testMary(Name *mary) {
  return mary->getName();
}
const char * testJames(Name *james) {
  return james->getName();
}
%}

//////////////////////////////////////////////////////////////////////////////////////
// Multi-arg typemap lookup
// One would never do something like this in reality, it just checks $typemap with multi-arg typemaps
%typemap(in) (Name *multiname, int num)($*1_type temp_name, $2_ltype temp_count)
%{
  /*%typemap(in) (Name *multiname, int num) start */
  temp_name = $*1_ltype("multiname num");
  temp_count = (int)strlen(temp_name.getNamePtr()->getName());
  (void)$input;
  $1 = temp_name.getNamePtr();
  $2 = temp_count + 100;
  /*%typemap(in) (Name *multiname, int num) end */
%}

%typemap(in) (Name *jim, int count) {
// %typemap(in) Name *jim start
$typemap(in, (Name *multiname, int num))
// %typemap(in) Name *jim end
}

%inline %{
const char * testJim(Name *jim, int count) {
  if (count != (int)strlen(jim->getNamePtr()->getName()) + 100)
    return "size check failed";
  else
    return jim->getName();
}
%}

//////////////////////////////////////////////////////////////////////////////////////
// Template types with more than one template parameter

// check $1 and $input get expanded properly when used from $typemap()
%typemap(in) Space::Pair<int, bool> PAIR_INT_BOOL ($1_type temp)
%{
  /*%typemap(in) Name *GENERIC start */
  temp = Space::Pair<int, bool>(123, true);
  (void)$input;
  $1 = ($1_ltype)temp;
  /*%typemap(in) Name *GENERIC end */
%}

%typemap(in) Space::Pair<int, bool> john {
// %typemap(in) Name *john start
$typemap(in, Space::Pair<int, bool> PAIR_INT_BOOL)
// %typemap(in) Name *john end
}

%inline %{
namespace Space {
  template <typename T1, typename T2> struct Pair {
    Pair(T1 f, T2 s) : first(f), second(s) {}
    Pair() {}
    T1 first;
    T2 second;
  };
  int testJohn(Space::Pair<int, bool> john) {
    return john.first;
  }
}
%}
%template(PairIntBool) Space::Pair<int, bool>;

//////////////////////////////////////////////////////////////////////////////////////
// A real use case for $typemap

#if defined(SWIGCSHARP)
%typemap(cscode) Space::RenameMe %{
  public static NewName factory(System.String s) {
  //below should expand to:
  //return new NewName( new Name(s) );
    return new $typemap(cstype, Space::RenameMe)( new $typemap(cstype, Name)(s) ); 
  }
%}
#elif defined(SWIGJAVA)
%typemap(javacode) Space::RenameMe %{
  public static NewName factory(String s) {
  //below should expand to:
  //return new NewName( new Name(s) );
    return new $typemap(jstype, Space::RenameMe)( new $typemap(jstype, Name)(s) ); 
  }
%}
#elif defined(SWIGD)
#if (SWIG_D_VERSION == 1)
%typemap(dcode) Space::RenameMe %{
  public static NewName factory(char[] s) {
    return new $typemap(dtype, Space::RenameMe)( new $typemap(dtype, Name)(s) );
  }
%}
#else
%typemap(dcode) Space::RenameMe %{
  public static NewName factory(string s) {
    return new $typemap(dtype, Space::RenameMe)( new $typemap(dtype, Name)(s) );
  }
%}
#endif
#endif

%rename(NewName) Space::RenameMe;
%inline %{
namespace Space {
  struct RenameMe {
    RenameMe(Name n) : storedName(n) {}
    Name getStoredName() { return storedName; }
  private:
    Name storedName;
  };
}
%}

