/******************************************************************************
 * $Id$
 *
 * Project:  MapServer
 * Purpose:  C#-specific enhancements to MapScript
 * Author:   Tamas Szekeres, szekerest@gmail.com
 *
 ******************************************************************************
 * Copyright (c) 1996-2008 Regents of the University of Minnesota.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in 
 * all copies of this Software or works derived from this Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *****************************************************************************/

/*Uncomment the following lines if you want to receive subsequent exceptions as
inner exceptions. Otherwise the exception message will be concatenated*/
//#if SWIG_VERSION >= 0x010329
//#define ALLOW_INNER_EXCEPTIONS
//#endif

%ignore fp;

/******************************************************************************
 * Implement Equals and GetHashCode properly
 *****************************************************************************/

%typemap(cscode) SWIGTYPE %{
  public override bool Equals(object obj) {
    if (obj == null)
        return false;
    if (this.GetType() != obj.GetType())
        return false;
    return swigCPtr.Handle.Equals($csclassname.getCPtr(($csclassname)obj).Handle);
  }

  public override int GetHashCode() {
    return swigCPtr.Handle.GetHashCode();
  }
%}

/******************************************************************************
 * Module initialization helper (bug 1665)
 *****************************************************************************/

%pragma(csharp) imclasscode=%{
  protected class $moduleHelper 
	{
		static $moduleHelper() 
		{
			$module.msSetup();
		}
		 ~$moduleHelper() 
		{
			//$module.msCleanup();
		}
	}
	protected static $moduleHelper the$moduleHelper = new $moduleHelper();
%}

/******************************************************************************
 * C# exception redefinition
 *****************************************************************************/
#ifdef ALLOW_INNER_EXCEPTIONS
%exception
{
	errorObj *ms_error;
	$action
    ms_error = msGetErrorObj();
    if (ms_error != NULL && ms_error->code != MS_NOERR) {
	    if (ms_error->code != MS_NOTFOUND && ms_error->code != -1) {
            int ms_errorcode = ms_error->code;
            while (ms_error!=NULL && ms_error->code != MS_NOERR) {
                char* msg =  msAddErrorDisplayString(NULL, ms_error);
                if (msg) {
			        SWIG_CSharpException(SWIG_SystemError, msg);
			        free(msg);
		        }
                else SWIG_CSharpException(SWIG_SystemError, "MapScript unknown error");
                ms_error = ms_error->next;	  
            }
            msResetErrorList();
            return $null;
        }
        msResetErrorList();
    }
}
#else
%exception
{
	errorObj *ms_error;
	$action
    ms_error = msGetErrorObj();
    if (ms_error != NULL && ms_error->code != MS_NOERR) {
	    if (ms_error->code != MS_NOTFOUND && ms_error->code != -1) {
            char* msg = msGetErrorString(";"); 
		    if (msg) {
			    SWIG_CSharpException(SWIG_SystemError, msg);
			    free(msg);
		    }
            else SWIG_CSharpException(SWIG_SystemError, "MapScript unknown error");
            msResetErrorList();
		    return $null;
        }
        msResetErrorList();
    }
}
#endif

/******************************************************************************
 * typemaps for string arrays (for supporting map templates)
 *****************************************************************************/
 
%pragma(csharp) imclasscode=%{
  public class StringArrayMarshal : IDisposable {
    public readonly IntPtr[] _ar;
    public StringArrayMarshal(string[] ar) {
      _ar = new IntPtr[ar.Length];
      for (int cx = 0; cx < _ar.Length; cx++) {
	      _ar[cx] = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(ar[cx]);
      }
    }
    public virtual void Dispose() {
	  for (int cx = 0; cx < _ar.Length; cx++) {
          System.Runtime.InteropServices.Marshal.FreeHGlobal(_ar[cx]);
      }
      GC.SuppressFinalize(this);
    }
  }
%}

%typemap(imtype, out="IntPtr") char** "IntPtr[]"
%typemap(cstype) char** %{string[]%}
%typemap(in) char** %{ $1 = ($1_ltype)$input; %}
%typemap(out) char** %{ $result = $1; %}
%typemap(csin) char** "new $modulePINVOKE.StringArrayMarshal($csinput)._ar"
%typemap(csout, excode=SWIGEXCODE) char** {
    $excode
    throw new System.NotSupportedException("Returning string arrays is not implemented yet.");
}
%typemap(csvarin, excode=SWIGEXCODE2) char** %{
    set {
      $excode
	  throw new System.NotSupportedException("Setting string arrays is not supported now.");
    } 
%}
/* specializations */
%typemap(csvarout, excode=SWIGEXCODE2) char** formatoptions %{
    get {
        IntPtr cPtr = $imcall;
        IntPtr objPtr;
	    string[] ret = new string[this.numformatoptions];
        for(int cx = 0; cx < this.numformatoptions; cx++) {
            objPtr = System.Runtime.InteropServices.Marshal.ReadIntPtr(cPtr, cx * System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)));
            ret[cx]= (objPtr == IntPtr.Zero) ? null : System.Runtime.InteropServices.Marshal.PtrToStringAnsi(objPtr);
        }
        $excode
        return ret;
    }
%}
%typemap(csvarout, excode=SWIGEXCODE2) char** values %{
    get {
        IntPtr cPtr = $imcall;
        IntPtr objPtr;
	    string[] ret = new string[this.numvalues];
        for(int cx = 0; cx < this.numvalues; cx++) {
            objPtr = System.Runtime.InteropServices.Marshal.ReadIntPtr(cPtr, cx * System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)));
            ret[cx]= (objPtr == IntPtr.Zero) ? null : System.Runtime.InteropServices.Marshal.PtrToStringAnsi(objPtr);
        }
        $excode
        return ret;
    }
%}

/******************************************************************************
 * typemaps for outputFormatObj arrays
 *****************************************************************************/

%typemap(ctype) outputFormatObj** "void*"
%typemap(imtype) outputFormatObj** "IntPtr"
%typemap(cstype) outputFormatObj** "outputFormatObj[]"
%typemap(out) outputFormatObj** %{ $result = $1; %}
%typemap(csout, excode=SWIGEXCODE) outputFormatObj** {
      IntPtr cPtr = $imcall;
	  IntPtr objPtr;
      outputFormatObj[] ret = new outputFormatObj[this.numoutputformats];
      for(int cx = 0; cx < this.numoutputformats; cx++) {
          objPtr = System.Runtime.InteropServices.Marshal.ReadIntPtr(cPtr, cx * System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)));
          ret[cx] = (objPtr == IntPtr.Zero) ? null : new outputFormatObj(objPtr, false, ThisOwn_false());
      }
      $excode
      return ret;
}

%typemap(csvarout, excode=SWIGEXCODE2) outputFormatObj** %{
    get {
	  IntPtr cPtr = $imcall;
	  IntPtr objPtr;
      outputFormatObj[] ret = new outputFormatObj[this.numoutputformats];
      for(int cx = 0; cx < this.numoutputformats; cx++) {
          objPtr = System.Runtime.InteropServices.Marshal.ReadIntPtr(cPtr, cx * System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)));
          ret[cx] = (objPtr == IntPtr.Zero) ? null : new outputFormatObj(objPtr, false, ThisOwn_false());
      }
      $excode
      return ret;
    }
%}

/******************************************************************************
 * gdBuffer Typemaps and helpers
 *****************************************************************************/

%pragma(csharp) imclasscode=%{
  public delegate void SWIGByteArrayDelegate(IntPtr data, int size);
%}

%insert(runtime) %{
/* Callback for returning byte arrays to C# */
typedef void (SWIGSTDCALL* SWIG_CSharpByteArrayHelperCallback)(const unsigned char *, const int);
/* Default callback interface */
static SWIG_CSharpByteArrayHelperCallback SWIG_csharp_bytearray_callback = NULL;
%}

%typemap(ctype) SWIG_CSharpByteArrayHelperCallback    %{SWIG_CSharpByteArrayHelperCallback%}
%typemap(imtype) SWIG_CSharpByteArrayHelperCallback  %{SWIGByteArrayDelegate%}
%typemap(cstype) SWIG_CSharpByteArrayHelperCallback %{$modulePINVOKE.SWIGByteArrayDelegate%}
%typemap(in) SWIG_CSharpByteArrayHelperCallback %{ $1 = ($1_ltype)$input; %}
%typemap(csin) SWIG_CSharpByteArrayHelperCallback "$csinput"

%csmethodmodifiers getBytes "private";
%ignore imageObj::getBytes();
%extend imageObj 
{
	void getBytes(SWIG_CSharpByteArrayHelperCallback callback) {
        gdBuffer buffer;
        
        buffer.owns_data = MS_TRUE;
        
        buffer.data = msSaveImageBuffer(self, &buffer.size, self->format);
            
        if( buffer.data == NULL || buffer.size == 0 )
        {
            msSetError(MS_MISCERR, "Failed to get image buffer", "getBytes");
            return;
        }
        callback(buffer.data, buffer.size);
        msFree(buffer.data);
	}
}

%ignore imageObj::write;

%typemap(cscode) imageObj %{
  private byte[] gdbuffer;
  private void CreateByteArray(IntPtr data, int size)
  {
      gdbuffer = new byte[size];
      Marshal.Copy(data, gdbuffer, 0, size);
  }
  
  public byte[] getBytes()
  {
	getBytes(new $modulePINVOKE.SWIGByteArrayDelegate(this.CreateByteArray));
	return gdbuffer;
  }
  
  public void write(System.IO.Stream stream)
  {
	getBytes(new $modulePINVOKE.SWIGByteArrayDelegate(this.CreateByteArray));
	stream.Write(gdbuffer, 0, gdbuffer.Length);
  }
%}


%typemap(ctype) gdBuffer    %{void%}
%typemap(imtype) gdBuffer  %{void%}
%typemap(cstype) gdBuffer %{byte[]%}

%typemap(out, null="") gdBuffer
%{ SWIG_csharp_bytearray_callback($1.data, $1.size);
	if( $1.owns_data ) gdFree($1.data); %}

// SWIGEXCODE is a macro used by many other csout typemaps
#ifdef SWIGEXCODE
%typemap(csout, excode=SWIGEXCODE) gdBuffer {
    $imcall;$excode
    return $modulePINVOKE.GetBytes();
}
#else
%typemap(csout) gdBuffer {
    $imcall;
    return $modulePINVOKE.GetBytes();
}
#endif

%pragma(csharp) imclasscode=%{
  protected class SWIGByteArrayHelper 
	{
		public delegate void SWIGByteArrayDelegate(IntPtr data, int size);
		static SWIGByteArrayDelegate bytearrayDelegate = new SWIGByteArrayDelegate(CreateByteArray);

		[DllImport("$dllimport", EntryPoint="SWIGRegisterByteArrayCallback_$module")]
		public static extern void SWIGRegisterByteArrayCallback_mapscript(SWIGByteArrayDelegate bytearrayDelegate);

		static void CreateByteArray(IntPtr data, int size) 
		{
			arraybuffer = new byte[size];
			Marshal.Copy(data, arraybuffer, 0, size);
		}

		static SWIGByteArrayHelper() 
		{
			SWIGRegisterByteArrayCallback_$module(bytearrayDelegate);
		}
	}
	protected static SWIGByteArrayHelper bytearrayHelper = new SWIGByteArrayHelper();
	[ThreadStatic] 
	private static byte[] arraybuffer;

	internal static byte[] GetBytes()
	{
		return arraybuffer;
	}
%}

%insert(runtime) %{
#ifdef __cplusplus
extern "C" 
#endif
#ifdef SWIGEXPORT
SWIGEXPORT void SWIGSTDCALL SWIGRegisterByteArrayCallback_$module(SWIG_CSharpByteArrayHelperCallback callback) {
  SWIG_csharp_bytearray_callback = callback;
}
#else
DllExport void SWIGSTDCALL SWIGRegisterByteArrayCallback_$module(SWIG_CSharpByteArrayHelperCallback callback) {
  SWIG_csharp_bytearray_callback = callback;
}
#endif
%}

/******************************************************************************
 * Preventing to take ownership of the memory when constructing objects 
 * with parent objects (causing nullreference exception, Bug 1743)
 *****************************************************************************/

%typemap(csconstruct, excode=SWIGEXCODE) layerObj(mapObj map) %{: this($imcall, true, map) {
  $excode
}
%}
%typemap(csconstruct, excode=SWIGEXCODE) classObj(layerObj layer) %{: this($imcall, true, layer) {
  $excode
}
%}
%typemap(csconstruct, excode=SWIGEXCODE) styleObj(classObj parent_class) %{: this($imcall, true, parent_class) {
  $excode
}
%}

%typemap(csout, excode=SWIGEXCODE) classObj* getClass, layerObj* getLayer, layerObj *getLayerByName, styleObj* getStyle {
    IntPtr cPtr = $imcall;
    $csclassname ret = (cPtr == IntPtr.Zero) ? null : new $csclassname(cPtr, $owner, ThisOwn_false());$excode
    return ret;
  }