/* 
 * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#ifndef _WF_BASE_H_
#define _WF_BASE_H_

#include <string>
#include <assert.h>

#include <mforms/base.h>
#include <mforms/view.h>
#include "ConvUtils.h"

#include <cairo/cairo.h>

// Define dummy structures for anonymous types used in cairo and glib, to give the compiler
// some food. That will prevent warnings like "unresolved typeref token ... image may not run".
// Functionally it makes no difference.
struct _cairo
{};

struct _cairo_surface
{};

struct _GMutex
{};

#pragma make_public(mforms::Object)

using namespace System;
using namespace Windows::Forms;
using namespace Collections::Generic;
using namespace Runtime::InteropServices;

#define DEFAULT_FONT "Tahoma"
#define DEFAULT_SMALL_FONT "Modern"
#define DEFAULT_FONT_SIZE 11

namespace MySQL {

// Callback definitions.
public ref class MCallback
{
public:
  ~MCallback();
};

template<typename R> public ref class MCallback0 : public MCallback
{
public:
  delegate R ManagedPtr();
  typedef R (__stdcall *UnmanagedPtr)();

  MCallback0(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  {
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback0::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer()); 
  }

private:  
  delegate R WrapperDelegate();

  ManagedPtr^ deleg;
  WrapperDelegate ^wrap_deleg;

  R cpp_callback()
  {
    return deleg();
  }
};

template<typename R,typename T1> public ref class MCallback1 : public MCallback
{
public:
  delegate R ManagedPtr(T1);
  typedef R (__stdcall *UnmanagedPtr)(T1);

  MCallback1(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  {
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback1::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer()); 
  }

private:  
  delegate R WrapperDelegate(T1);

  ManagedPtr^ deleg;
  WrapperDelegate ^wrap_deleg;

  R cpp_callback(T1 arg1)
  {
    return deleg(arg1);
  }
};

template<typename R,typename T1,typename T2> public ref class MCallback2 : public MCallback
{
public:
  delegate R ManagedPtr(T1, T2);
  typedef R (__stdcall *UnmanagedPtr)(T1,T2);

  MCallback2(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback2::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer()); 
  }

private:  
  delegate R WrapperDelegate(T1 a1, T2 a2);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  R cpp_callback(T1 a1, T2 a2)
  {
    return deleg(a1, a2);
  }
};


template<typename T1,typename T2> public ref class MCallback2<void,T1,T2> : public MCallback
{
public:
  delegate void ManagedPtr(T1, T2);
  typedef void (__stdcall *UnmanagedPtr)(T1,T2);

  MCallback2(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback2::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer()); 
  }

private:  
  delegate void WrapperDelegate(T1, T2);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  void cpp_callback(T1 a1, T2 a2)
  {
    deleg(a1, a2);
  }
};



template<typename R,typename T1,typename T2,typename T3> public ref class MCallback3 : public MCallback
{
public:
  delegate R ManagedPtr(T1, T2, T3);
  typedef R (__stdcall *UnmanagedPtr)(T1,T2,T3);

  MCallback3(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback3::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer()); 
  }

private:  
  delegate R WrapperDelegate(T1 a1, T2 a2, T3 a3);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  R cpp_callback(T1 a1, T2 a2, T3 a3)
  {
    return deleg(a1, a2, a3);
  }
};


template<typename R,typename T1,typename T2,typename T3,typename T4> public ref class MCallback4 : public MCallback
{
public:
  delegate R ManagedPtr(T1, T2, T3, T4);
  typedef R (__stdcall *UnmanagedPtr)(T1,T2,T3,T4);

  MCallback4(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback4::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer()); 
  }

private:  
  delegate R WrapperDelegate(T1 a1, T2 a2, T3 a3, T4 a4);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  R cpp_callback(T1 a1, T2 a2, T3 a3, T4 a4)
  {
    return deleg(a1, a2, a3, a4);
  }
};


template<typename R,typename T1,typename T2,typename T3,typename T4,typename T5> public ref class MCallback5 : public MCallback
{
public:
  delegate R ManagedPtr(T1, T2, T3, T4, T5);
  typedef R (__stdcall *UnmanagedPtr)(T1,T2,T3,T4,T5);

  MCallback5(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback5::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer());
  }

private:  
  delegate R WrapperDelegate(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  R cpp_callback(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5)
  {
    return deleg(a1, a2, a3, a4, a5);
  }
};

template<typename R,typename T1,typename T2,typename T3,typename T4,typename T5, typename T6> 
public ref class MCallback6 : public MCallback
{
public:
  delegate R ManagedPtr(T1, T2, T3, T4, T5, T6);
  typedef R (__stdcall *UnmanagedPtr)(T1, T2, T3, T4, T5, T6);

  MCallback6(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback6::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer());
  }

private:  
  delegate R WrapperDelegate(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  R cpp_callback(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6)
  {
    return deleg(a1, a2, a3, a4, a5, a6);
  }
};

template<typename R,typename T1,typename T2,typename T3,typename T4,typename T5, typename T6, typename T7> 
public ref class MCallback7 : public MCallback
{
public:
  delegate R ManagedPtr(T1, T2, T3, T4, T5, T6, T7);
  typedef R (__stdcall *UnmanagedPtr)(T1, T2, T3, T4, T5, T6, T7);

  MCallback7(ManagedPtr ^ptr)
    :deleg(ptr)
  {
  }

  UnmanagedPtr get_pointer() 
  { 
    wrap_deleg= gcnew WrapperDelegate(this, &MCallback7::cpp_callback);
    return static_cast<UnmanagedPtr>(Marshal::GetFunctionPointerForDelegate(wrap_deleg).ToPointer());
  }

private:  
  delegate R WrapperDelegate(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7);

  ManagedPtr^ deleg;
  WrapperDelegate^ wrap_deleg;

  R cpp_callback(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7)
  {
    return deleg(a1, a2, a3, a4, a5, a6, a7);
  }
};

#define DEF_CALLBACK0(R, mgr, strukt, klass, method) do {\
  MCallback0<R> ^method= gcnew MCallback0<R>(gcnew MCallback0<R>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK1(R,T1,mgr,strukt,klass,method) do {\
  MCallback1<R,T1> ^method= gcnew MCallback1<R,T1>(gcnew MCallback1<R,T1>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK2(R,T1,T2,mgr,strukt,klass,method) do {\
  MCallback2<R,T1,T2> ^method= gcnew MCallback2<R,T1,T2>(gcnew MCallback2<R,T1,T2>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK3(R,T1,T2,T3,mgr,strukt,klass,method) do {\
  MCallback3<R,T1,T2,T3> ^method= gcnew MCallback3<R,T1,T2,T3>(gcnew MCallback3<R,T1,T2,T3>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK4(R,T1,T2,T3,T4,mgr,strukt,klass,method) do {\
  MCallback4<R,T1,T2,T3,T4> ^method= gcnew MCallback4<R,T1,T2,T3,T4>(gcnew MCallback4<R,T1,T2,T3,T4>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK5(R,T1,T2,T3,T4,T5,mgr,strukt,klass,method) do {\
  MCallback5<R,T1,T2,T3,T4,T5> ^method= gcnew MCallback5<R,T1,T2,T3,T4,T5>(gcnew MCallback5<R,T1,T2,T3,T4,T5>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK6(R, T1, T2, T3, T4, T5, T6, mgr, strukt, klass, method) do {\
  MCallback6<R, T1, T2, T3, T4, T5, T6> ^method= gcnew MCallback6<R, T1, T2, T3, T4, T5, T6>(\
  gcnew MCallback6<R, T1, T2, T3, T4, T5, T6>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

#define DEF_CALLBACK7(R, T1, T2, T3, T4, T5, T6, T7, mgr, strukt, klass, method) do {\
  MCallback7<R, T1, T2, T3, T4, T5, T6, T7> ^method= gcnew MCallback7<R, T1, T2, T3, T4, T5, T6, T7>(\
  gcnew MCallback7<R, T1, T2, T3, T4, T5, T6, T7>::ManagedPtr(klass::method));\
  mgr->keep_callback(method);\
  strukt.method= method->get_pointer();\
} while (0)

namespace Forms {
  public ref class ObjectImpl
  {
  private:
    GCHandle m_gch;

    // These members represent the .NET class for which this wrapper class was created.
    // Neither of these classes have a common ancestor which has a Tag property (which we need
    // to store back references. Only one of them must have a value actually.
    Windows::Forms::Control^ control;
    Windows::Forms::CommonDialog^ dialog;
    Windows::Forms::ToolStripItem^ toolStripItem;

    // Returns a fixed pointer to this object that will not be modified by the GC
    IntPtr GetFixedId()
    {
      if (!m_gch.IsAllocated)
         m_gch = GCHandle::Alloc(this);
       return GCHandle::ToIntPtr(m_gch);
    }

    // Needs to be called when destroying the object
    void ReleaseHandle()
    {
      m_gch.Free();
    }

  protected:
    ObjectImpl(::mforms::Object *object)
    {
      control= nullptr;
      dialog= nullptr;
      toolStripItem = nullptr;

      // Get a fixed pointer to this object.
      IntPtr ip = this->GetFixedId();

      // Store this pointer in the backend object as mapping from backend to wrapper.
      object->set_data((void*)(intptr_t) ip);
    }

    virtual ~ObjectImpl()
    {
      ReleaseHandle();

      delete control;
      delete dialog;
      delete toolStripItem;
    }

    virtual void initialize() {};

    // Returns the object based on the fixed pointer retrieved by GetFixedId()
    static ObjectImpl^ GetFromFixedId(IntPtr ip)
    {
      GCHandle gcHandle = GCHandle::FromIntPtr(ip);
      return (ObjectImpl^)gcHandle.Target;
    }

  public:
    static ObjectImpl^ FromUnmanaged(const mforms::Object *object)
    {
      return GetFromFixedId((IntPtr)object->get_data_ptr());
    }

    /**
      * Creates a new instance of a .NET control of type T for the given backend object and returns it.
      * The backend object pointer is stored in the Tag property of the .NET control for later back reference.
      * For the same reason the wrapper gets a reference of the .NET control.
      */
    template<class T>
    static T^ create(::mforms::Object* backend, ObjectImpl^ wrapper)
    {
      assert(wrapper->dialog == nullptr);
      assert(wrapper->toolStripItem == nullptr);

      T^ control= gcnew T();
      control->Tag= gcnew IntPtr(backend);
      wrapper->control= control;

      // Do some common setup for the control.
      control->AutoSize= false;
      control->Size= control->MinimumSize;
      control->Font= gcnew Font(DEFAULT_FONT, DEFAULT_FONT_SIZE, FontStyle::Regular, GraphicsUnit::Pixel);

      wrapper->initialize();

      return control;
    }

    template<class T>
    static T^ create(::mforms::Object* backend, ObjectImpl^ wrapper, ToolStripItem^)
    {
      assert(wrapper->dialog == nullptr);
      assert(wrapper->control == nullptr);

      ToolStripItem^ item= gcnew T();
      item->Tag= gcnew IntPtr(backend);
      wrapper->toolStripItem= item;
      wrapper->initialize();

      return (T^) item;
    }

    /**
      * Creates a new instance of a common dialog of type T for the given backend View and returns it.
      * Otherwise it is the same as the create<T> function.
      */
    template<class T>
    static T^ create_dialog(::mforms::Object* backend, ObjectImpl^ wrapper)
    {
      assert(wrapper->control == nullptr);
      assert(wrapper->toolStripItem == nullptr);

      T^ dialog= gcnew T();
      dialog->Tag= gcnew IntPtr(backend);
      wrapper->dialog= dialog;
      wrapper->initialize();

      return dialog;
    }

    // Returns the .NET control or dialog connected to this wrapper.
    template<class T>
    T^ get_control()
    {
      if (control != nullptr)
        return dynamic_cast<T^>(control);
      else
        if (toolStripItem != nullptr)
          return dynamic_cast<T^>(toolStripItem);
        else
          return dynamic_cast<T^>(dialog);
    }

    // A variant without template for use by other assemblies.
    Control^ get_control()
    {
      return get_control<Control>();
    }

    // Returns the backend object which is wrapped by the wrapper class the given
    // control was created for.
    template<class T>
    static T* get_backend_control(Object^ object)
    {
      Control^ control= dynamic_cast<Control^>(object);
      if (control != nullptr)
        return reinterpret_cast<T*>(((IntPtr^)control->Tag)->ToPointer());
      else
      {
        CommonDialog^ dialog= dynamic_cast<CommonDialog^>(object);
        if (dialog != nullptr)
          return reinterpret_cast<T*>(((IntPtr^)dialog->Tag)->ToPointer());
        else
        {
          ToolStripItem^ item = dynamic_cast<ToolStripItem^>(object);
          if (item != nullptr)
            return reinterpret_cast<T*>(((IntPtr^) item->Tag)->ToPointer());
          return NULL;
        }
      }
    }
  };
};


};


#endif
