#ifndef __GRT_MANAGER_H__
#define __GRT_MANAGER_H__

#include "TreeModel.h"
#include "GrtShell.h"
#include "GrtValueTree.h"
#include "GrtValueInspector.h"
#include "GrtStructsTree.h"
#include "GrtModulesTree.h"
#include "GrtMessageList.h"

namespace MySQL {
  namespace Grt {

    public ref class GrtManager 
    {
    public:
      delegate void VoidStringStringDelegate(String^ str1, String^ str2);
      delegate void VoidIntStringStringDelegate(int i, String^ str1, String^ str2);
      delegate void VoidStringDelegate(String^ str);
      delegate bool BoolStringStringFloatDelegate(String^ str1, String^ str2, float f);

    private:
      delegate void VoidStringStringWrapperDelegate(const std::string& str1, const std::string& str2);
      typedef void (*GrtManager::VOID_STRING_STRING_HANDLER_CB)(const std::string& str1, const std::string& str2);

      delegate void VoidIntStringStringWrapperDelegate(int i, const std::string& str1, const std::string& str2);
      typedef void (*GrtManager::VOID_INT_STRING_STRING_HANDLER_CB)(int i, const std::string& str1, const std::string& str2);

      delegate void VoidStringWrapperDelegate(const std::string& str);
      typedef void (*GrtManager::VOID_STRING_HANDLER_CB)(const std::string& str);

      delegate bool BoolStringStringFloatWrapperDelegate(const std::string& str1, const std::string& str2, float f);
      typedef bool (*GrtManager::BOOL_STRING_STRING_FLOAT_HANDLER_CB)(const std::string& str1, const std::string& str2, float f);

      ::bec::GRTManager *inner;
      GrtShell^ managed_shell;
      GRT^ managed_grt;

      VoidStringStringDelegate^ error_callback_delegate;
      VoidStringStringWrapperDelegate^ error_callback_wrapper_delegate;

      void error_callback_wrapper(const std::string& str1, const std::string& str2)
      { error_callback_delegate(CppStringToNative(str1), CppStringToNative(str2)); }

      VoidIntStringStringDelegate^ message_callback_delegate;
      VoidIntStringStringWrapperDelegate^ message_callback_wrapper_delegate;

      void message_callback_wrapper(int i, const std::string& str1, const std::string& str2)
      { message_callback_delegate(i, CppStringToNative(str1), CppStringToNative(str2)); }

      VoidStringDelegate^ output_callback_delegate;
      VoidStringWrapperDelegate^ output_callback_wrapper_delegate;

      void output_callback_wrapper(const std::string& str)
      { output_callback_delegate(CppStringToNative(str)); }

      VoidStringDelegate^ shell_output_callback_delegate;
      VoidStringWrapperDelegate^ shell_output_callback_wrapper_delegate;

      void shell_output_callback_wrapper(const std::string& str)
      { shell_output_callback_delegate(CppStringToNative(str)); }

      BoolStringStringFloatDelegate^ progress_callback_delegate;
      BoolStringStringFloatWrapperDelegate^ progress_callback_wrapper_delegate;

      bool progress_callback_wrapper(const std::string& str1, const std::string& str2, float f)
      { return progress_callback_delegate(CppStringToNative(str1), CppStringToNative(str2), f); }

    public:
      GrtManager(bool threaded, bool verbose)
        : inner(new ::bec::GRTManager(threaded, verbose)), managed_shell(nullptr), managed_grt(nullptr)
      {}

      GrtManager(::bec::GRTManager *grt_manager)
        : inner(grt_manager), managed_shell(nullptr), managed_grt(nullptr)
      {}

      explicit GrtManager()
        : inner(nullptr), managed_shell(nullptr), managed_grt(nullptr)
      {}

      virtual ~GrtManager()
      { /*delete inner;*/ }

      ::bec::GRTManager *get_unmanaged_object()
      { return static_cast<::bec::GRTManager *>(inner); }

      String^ get_app_option_string(String^ option_name)
      { return CppStringToNative(inner->get_app_option_string(NativeToCppString(option_name))); }

      Font^ get_font_option(String^ option_name)
      {
        String^ font_string= CppStringToNative(inner->get_app_option_string(NativeToCppString(option_name))); 

        // Parse font string into its details and create a new Font instance.
        array<String^>^ parts= font_string->Split(' ');
        int index = parts->Length - 1;

        int fontSize = 12;
        if (index > 0)
        {
          try
          {
            fontSize= int::Parse(parts[index]);
            --index;
          }
          catch (...)
          {
            // Swallow any conversion error.
          }
        }

        FontStyle style = FontStyle::Regular;
        for (int i = 0; i < 2; i++)
        {
          if (index > 0)
          {
            if (parts[index]->ToLower() == "bold")
            {
              style = style | FontStyle::Bold;
              --index;
            }
            else 
              if (parts[index]->ToLower() == "italic")
              {
                style = style | FontStyle::Italic;
                --index;
              }
          }
        }

        String^ family = parts[0];
        for (int i = 1; i <= index; i++)
          family += " " + parts[i];

        return gcnew Font(family, (float) fontSize, style);
      }

      void set_module_extensions(List<String^>^ extensions)
      {
        inner->set_module_extensions(NativeToCppStringList2(extensions));
      }

      // looks this is because ::bec::GRTManager is private type of Grt wrapper module,
      // so I need to cast it to void *, otherwise compiler gives 'method inaccessible' error
      //void set_unmanaged_object(void *inn)
      //{ inner= static_cast<::bec::GRTManager *>(inn); }

      void set_search_paths(String^ module_sp, String^ struct_sp, String^ libs_sp)
      {
        inner->set_search_paths(
          NativeToCppString(module_sp),
          NativeToCppString(struct_sp),
          NativeToCppString(libs_sp));
      }

      void set_error_callback(VoidStringStringDelegate^ dt)
      {
        error_callback_delegate= dt;
        error_callback_wrapper_delegate = 
          gcnew VoidStringStringWrapperDelegate(this, &GrtManager::error_callback_wrapper);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(error_callback_wrapper_delegate);
        VOID_STRING_STRING_HANDLER_CB cb = static_cast<VOID_STRING_STRING_HANDLER_CB>(ip.ToPointer());
        inner->set_error_callback(sigc::ptr_fun(cb));
      }

      /*
      void set_message_callback(VoidIntStringStringDelegate^ dt)
      {
        message_callback_delegate= dt;
        message_callback_wrapper_delegate = 
          gcnew VoidStringStringWrapperDelegate(this, &GrtManager::message_callback_wrapper);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(message_callback_wrapper_delegate);
        VOID_TRING_STRING_HANDLER_CB cb = static_cast<VOID_STRING_STRING_HANDLER_CB>(ip.ToPointer());
        inner->set_message_callback(sigc::ptr_fun(cb));
      }*/

      void set_output_callback(VoidStringDelegate^ dt)
      {
        output_callback_delegate= dt;
        output_callback_wrapper_delegate = 
          gcnew VoidStringWrapperDelegate(this, &GrtManager::output_callback_wrapper);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(output_callback_wrapper_delegate);
        VOID_STRING_HANDLER_CB cb = static_cast<VOID_STRING_HANDLER_CB>(ip.ToPointer());
        //inner->set_output_callback(sigc::ptr_fun(cb));
      }

      void set_shell_output_callback(VoidStringDelegate^ dt)
      {
        shell_output_callback_delegate= dt;
        shell_output_callback_wrapper_delegate = 
          gcnew VoidStringWrapperDelegate(this, &GrtManager::shell_output_callback_wrapper);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(shell_output_callback_wrapper_delegate);
        VOID_STRING_HANDLER_CB cb = static_cast<VOID_STRING_HANDLER_CB>(ip.ToPointer());
        //inner->set_shell_output_callback(sigc::ptr_fun(cb));
      }

      //void set_progress_callback(const sigc::slot<void,std::string,std::string,float> &cb);
      void set_progress_callback(BoolStringStringFloatDelegate^ dt)
      {
        progress_callback_delegate= dt;
        progress_callback_wrapper_delegate = 
          gcnew BoolStringStringFloatWrapperDelegate(this, &GrtManager::progress_callback_wrapper);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(progress_callback_wrapper_delegate);
        BOOL_STRING_STRING_FLOAT_HANDLER_CB cb = static_cast<BOOL_STRING_STRING_FLOAT_HANDLER_CB>(ip.ToPointer());
        inner->set_progress_callback(sigc::ptr_fun(cb));
      }

      //void set_basedir(String^ basedir)
      //  { inner->set_basedir(NativeToCppString(basedir)); }

      //GRTDispatcher *get_dispatcher() const { return _dispatcher; }

      GRT^ get_grt() 
      { 
        if(managed_grt == nullptr)
          managed_grt= gcnew GRT(inner->get_grt()); 
        return managed_grt;
      }

      void initialize()
      { inner->initialize(); }

      void perform_idle_tasks()
      { inner->perform_idle_tasks(); }

      void rescan_modules()
      { inner->rescan_modules(); }

      //GRTObjectRegistry *get_registry() const { return _registry; }

      // shell
      GrtShell^ get_shell()
      { 
        if(managed_shell == nullptr)
          managed_shell= gcnew GrtShell(inner->get_shell());
        return managed_shell; 
      }

      // grt internal state inspectors
      GrtValueTree^ get_shared_value_tree(String ^valuespec)
      { return gcnew GrtValueTree(inner->get_shared_value_tree(NativeToCppString(valuespec))); }

      GrtValueInspector^ get_new_value_inspector(GrtValue^ value, bool process_editas_flag)
      {
        return gcnew GrtValueInspector(inner->get_new_value_inspector(value->get_unmanaged_object(), process_editas_flag), false);
      }

      GrtMessageList^ get_messages_list()
      {
        return gcnew GrtMessageList(inner->get_messages_list());
      }

      //GrtValueInspector *get_grouped_object_inspector(MYX_GRT_VALUE *object);

      GrtStructsTree^ get_shared_structs_tree()
      { return gcnew GrtStructsTree(inner->get_shared_structs_tree()); }

      GrtModulesTree^ get_shared_modules_tree()
      { return gcnew GrtModulesTree(inner->get_shared_modules_tree()); }

      void terminate() { inner->terminate(); };
      bool terminated() { return inner->terminated(); };
      void resetTermination() { inner->reset_termination(); };
    };

  } // namespace Grt
} // namespace MySQL

#endif // __GRT_MANAGER_H__