/* 
 * Copyright (c) 2010, 2011, 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_APP_H_
#define _WF_APP_H_

#include "mforms/app.h"
#include "workbench/wb_context.h"

#include "wf_appview.h"

#pragma make_public(mforms::App)
#pragma make_public(wb::WBFrontendCallbacks)

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices; // Needed for the [Out] keyword.

using namespace MySQL::Controls;
using namespace MySQL::Grt;
using namespace MySQL::GUI::Mdc;

namespace MySQL {
  namespace Forms {

    public enum class AppCommand
    {
      AppDockView,
      AppSelectView,
      AppUndockView,
      AppSetViewTitle,
      AppQuit,
      AppGetResourcePath,
      AppSetStatusText,
      AppGetBounds,
    };

    public enum class RefreshType 
    {
      RefreshNeeded               = wb::RefreshNeeded,

      RefreshNothing              = wb::RefreshNothing,
      RefreshSchemaList           = wb::RefreshSchemaList,
      RefreshSchema               = wb::RefreshSchema,
      RefreshSchemaNoReload       = wb::RefreshSchemaNoReload,
      RefreshNewDiagram           = wb::RefreshNewDiagram,
      RefreshSelection            = wb::RefreshSelection,
      RefreshCloseEditor          = wb::RefreshCloseEditor,
      RefreshFindOutput           = wb::RefreshFindOutput,
      RefreshNewModel             = wb::RefreshNewModel,
      RefreshOverviewNodeInfo     = wb::RefreshOverviewNodeInfo,
      RefreshOverviewNodeChildren = wb::RefreshOverviewNodeChildren,
      RefreshViewName             = wb::RefreshViewName,
      RefreshDocument             = wb::RefreshDocument,
      RefreshCloseDocument        = wb::RefreshCloseDocument,
      RefreshZoom                 = wb::RefreshZoom,
      RefreshTimer                = wb::RefreshTimer,
      RefreshFinishEdits          = wb::RefreshFinishEdits,
    };

    public enum class GUIPluginFlags
    {
      ForceNewWindowFlag=   ::bec::ForceNewWindowFlag,
      StandaloneWindowFlag= ::bec::StandaloneWindowFlag
    };

    // A managed version of the App class.
    public ref class AppImpl
    {
    public:
      // Delegates used by C#.
      delegate void VoidDelegate();
      delegate void VoidBoolDelegate(bool f);
      delegate void VoidStrDelegate(String^ str1);
      delegate void VoidStrStrDelegate(String^ str1, String^ str2);
      delegate String^ StrStrStrStrDelegate(String^ str1, String^ str2, String^ str3);
      delegate bool BoolStrStrStrDelegate(String^ str1, String^ str2, String^ str3);
      delegate bool BoolStrStrStrStrDelegate(String^ str0, String^ str1, String^ str2, String^ str3);
      delegate bool BoolStrStrFloatDelegate(String^ str1, String^ str2, float f3);
      delegate bool BoolStrIntStrPtrDelegate(String^ str, int i, [Out] String^ %result);
      delegate BaseWindowsCanvasView^ CanvasViewStringStringIntPtrDelegate(String^ str1, String^ str2, IntPtr payload);
      delegate void VoidCanvasViewDelegate(BaseWindowsCanvasView^ canvasView);
      delegate IntPtr IntPtrGRTManagerModuleStrStrGrtListFlagsDelegate(GrtManager^ grtManager, GrtModule^ module, String^ str1, String^ str2, GrtValue^ grtlist, GUIPluginFlags flags);
      delegate void VoidIntPtrDelegate(IntPtr p2);
      delegate void VoidRefreshTypeStringIntPtrDelegate(RefreshType refresh, String^ str, IntPtr ptr);
      delegate String^ StringAppCommandAppViewStringDelegate(AppCommand, MySQL::Forms::AppViewImpl^ view, String^ str);

    private:
      ::wb::WBFrontendCallbacks* _callbacks;
      GCHandle _cb_handle; // Keeps an unmanaged reference to this class for mforms::App callbacks.

      // ----------------------------------------------------------------------------
      // Wrapper delegates used by the C++.net wrapper

      void set_show_file_dialog(StrStrStrStrDelegate^ dt);
      void set_show_status_text(VoidStrDelegate^ dt);
      void set_show_progress(BoolStrStrFloatDelegate^ dt);
      void set_request_input(BoolStrIntStrPtrDelegate^ dt);
      void set_create_diagram(CanvasViewStringStringIntPtrDelegate^ dt);
      void set_destroy_view(VoidCanvasViewDelegate^ dt);
      void set_switched_view(VoidCanvasViewDelegate^ dt);
      void set_tool_changed(VoidCanvasViewDelegate^ dt);
      void set_open_editor(IntPtrGRTManagerModuleStrStrGrtListFlagsDelegate^ dt);
      void set_show_editor(VoidIntPtrDelegate^ dt);
      void set_hide_editor(VoidIntPtrDelegate^ dt);
      void set_refresh_gui(VoidRefreshTypeStringIntPtrDelegate ^ dt);
      void set_perform_command(VoidStrDelegate ^ dt);
      void set_lock_gui(VoidBoolDelegate^ dt);
      void set_quit_application(VoidDelegate^ dt);

      // void ()
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidWrapperDelegate();
      typedef void (*AppImpl::VOID_CB)();

      // void (bool)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidBoolWrapperDelegate(bool b);
      typedef void (*AppImpl::VOID_BOOL_CB)(bool b);

      // void (string)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidStrWrapperDelegate(const std::string& str1);
      typedef void (*AppImpl::VOID_STR_CB)(const std::string& str1);

      // void (string, string)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidStrStrWrapperDelegate(const std::string& str1, const std::string& str2);
      typedef void (*AppImpl::VOID_STR_STR_CB)(const std::string& str1, const std::string& str2);

      // string (string, string, string)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate std::string StrStrStrStrWrapperDelegate(const std::string& str1, const std::string& str2, const std::string& str3);
      typedef std::string (*AppImpl::STR_STR_STR_STR_CB)(const std::string& str1, const std::string& str2, const std::string& str3);

      // bool (string, string, string)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate bool BoolStrStrStrWrapperDelegate(const std::string& str1, const std::string& str2, const std::string& str3);
      typedef bool (*AppImpl::BOOL_STR_STR_STR_CB)(const std::string& str1, const std::string& str2, const std::string& str3);

      // int (string, string, string, string, string)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate int IntStrStrStrStrStrWrapperDelegate(const std::string& title, const std::string& str1, const std::string& str2, const std::string& str3, const std::string& str4);
      typedef int (*AppImpl::INT_STR_STR_STR_STR_STR_CB)(const std::string& title, const std::string& str1, const std::string& str2, const std::string& str3, const std::string& str4);

      // bool (string, string, float)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate bool BoolStrStrFloatWrapperDelegate(const std::string& str1, const std::string& str2, float f3);
      typedef bool (*AppImpl::BOOL_STR_STR_FLOAT_CB)(const std::string& str1, const std::string& str2, float f3);

      // bool (string, int, string&)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate bool BoolStrIntStrPtrWrapperDelegate(const std::string &str, int, std::string &res);
      typedef bool (*AppImpl::BOOL_STR_INT_STRPTR_CB)(const std::string &str, int, std::string &res);

      // CanvasView * (model_DiagramRef&)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate ::mdc::CanvasView* CanvasViewDiagramWrapperDelegate(const model_DiagramRef& diagram);
      typedef ::mdc::CanvasView* (*AppImpl::CANVASVIEW_DIAGRAM_CB)(const model_DiagramRef& diagram);

      // void (CanvasView *)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidCanvasViewWrapperDelegate(::mdc::CanvasView *canvas_view);
      typedef void (*AppImpl::VOID_CANVASVIEW_CB)(::mdc::CanvasView *canvas_view);

      // _int64 (GrtManager *, Module*, string, string, GrtList, GUIPluginFlags)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate uintptr_t IntGRTManagerModuleStrStrGrtListFlagsWrapperDelegate(::bec::GRTManager* grt_manager, grt::Module *, const std::string& str2, const std::string& str3, const grt::BaseListRef &grt_list, bec::GUIPluginFlags flags);
      typedef uintptr_t (*AppImpl::INT_GRTMANAGER_MODULE_STR_STR_GRTLIST_FLAGS_CB)(::bec::GRTManager* grt_manager, grt::Module *module, const std::string& str2, const std::string& str3, const grt::BaseListRef &grt_list, bec::GUIPluginFlags flags);

      // void (uintptr_t)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidIntPtrWrapperDelegate(uintptr_t native_handle);
      typedef void (*AppImpl::VOID_INT64_CB)(uintptr_t native_handle);
      
      // void (RefreshType, string, uintptr_t)
      [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
      delegate void VoidRefreshTypeStringIntPtrWrapperDelegate(::wb::RefreshType refresh, const std::string &str, uintptr_t ptr);
      typedef void (*AppImpl::VOID_REFRESHTYPE_STRING_INTPTR_CB)(::wb::RefreshType refresh, const std::string &str, uintptr_t ptr);
      
      // ----------------------------------------------------------------------------
      // Variables used to store the C# delegates and the C++.Net wrapper delegates
      // Intermediate callback that is called from C++ and calls the C# delegate

      BoolStrIntStrPtrDelegate^ request_input_delegate;
      BoolStrIntStrPtrWrapperDelegate^ request_input_wrapper_delegate;
      bool request_input_wrapper(const std::string &title, int flags, std::string& res);

      StrStrStrStrDelegate^ show_file_dialog_delegate;
      StrStrStrStrWrapperDelegate^ show_file_dialog_wrapper_delegate;
      std::string show_file_dialog_wrapper(const std::string& str1, const std::string& str2,
        const std::string& str3);

      VoidStrDelegate^ show_status_text_delegate;
      VoidStrWrapperDelegate^ show_status_text_wrapper_delegate;
      void show_status_text_wrapper(const std::string& str1);

      BoolStrStrFloatDelegate^ show_progress_delegate;
      BoolStrStrFloatWrapperDelegate^ show_progress_wrapper_delegate;
      bool show_progress_wrapper(const std::string& str1, const std::string& str2, float f3);

      VoidStrDelegate^ shell_output_delegate;
      VoidStrWrapperDelegate^ shell_output_wrapper_delegate;
      void shell_output_wrapper(const std::string& str1);

      // CanvasView
      CanvasViewStringStringIntPtrDelegate^ create_diagram_delegate;
      CanvasViewDiagramWrapperDelegate^ create_diagram_wrapper_delegate;
      ::mdc::CanvasView *create_diagram_wrapper(const model_DiagramRef& model);

      VoidCanvasViewDelegate^ destroy_view_delegate;
      VoidCanvasViewWrapperDelegate^ destroy_view_wrapper_delegate;
      void destroy_view_wrapper(::mdc::CanvasView *canvas_view);

      VoidCanvasViewDelegate^ switched_view_delegate;
      VoidCanvasViewWrapperDelegate^ switched_view_wrapper_delegate;
      void switched_view_wrapper(::mdc::CanvasView *canvas_view);

      VoidCanvasViewDelegate^ tool_changed_delegate;
      VoidCanvasViewWrapperDelegate^ tool_changed_wrapper_delegate;
      void tool_changed_wrapper(::mdc::CanvasView *canvas_view);

      // Editors
      IntPtrGRTManagerModuleStrStrGrtListFlagsDelegate^ open_editor_delegate;
      IntGRTManagerModuleStrStrGrtListFlagsWrapperDelegate^ open_editor_wrapper_delegate;
      uintptr_t open_editor_wrapper(::bec::GRTManager* grt_manager, grt::Module *module,
        const std::string& str2, const std::string& str3, const grt::BaseListRef &grt_list,
        bec::GUIPluginFlags flags);

      VoidIntPtrDelegate^ show_editor_delegate;
      VoidIntPtrWrapperDelegate^ show_editor_wrapper_delegate;
      void show_editor_wrapper(uintptr_t native_handle);

      VoidIntPtrDelegate^ hide_editor_delegate;
      VoidIntPtrWrapperDelegate^ hide_editor_wrapper_delegate;
      void hide_editor_wrapper(uintptr_t native_handle);

      // UI related callbacks.
      VoidRefreshTypeStringIntPtrDelegate ^refresh_gui_delegate;
      VoidRefreshTypeStringIntPtrWrapperDelegate ^refresh_gui_wrapper_delegate;
      void refresh_gui_wrapper(::wb::RefreshType refresh, const std::string &str, uintptr_t ptr);

      VoidStrDelegate ^perform_command_delegate;
      VoidStrWrapperDelegate ^perform_command_wrapper_delegate;
      void perform_command_wrapper(const std::string &command);

      VoidBoolDelegate^ lock_gui_delegate;
      VoidBoolWrapperDelegate^ lock_gui_wrapper_delegate;
      void lock_gui_wrapper(bool flag);

      VoidDelegate^ quit_application_delegate;
      VoidWrapperDelegate^ quit_application_wrapper_delegate;
      void quit_application_wrapper();

      StringAppCommandAppViewStringDelegate^ app_command_delegate;
      std::string app_command_wrapper(AppCommand command, mforms::AppView* view, const std::string &str);

    protected:
      static Object^ GetFromFixedId(IntPtr ip);

      static void dock_view(mforms::App *app, mforms::AppView *view, const std::string &position);
      static bool select_view(mforms::App *app, const std::string &identifier);
      static void undock_view(mforms::App *app, mforms::AppView *view);
      static void set_view_title(mforms::App *app, mforms::AppView *view, const std::string &title);
      static std::string get_resource_path(mforms::App *app, const std::string &file);
      static void set_status_text(mforms::App *app, const std::string &text);
      static std::string get_bounds(mforms::App *app);
      static int enter_event_loop(mforms::App *app, float max_wait_time);
      static void exit_event_loop(mforms::App *app, int ret_code);

      static MySQL::Drawing::Color get_system_color(mforms::SystemColor type);

    public:
      // TODO: historically grown separation of certain application commands and callbacks.
      //       This should be rewritten to remove the need of a separate callbacks class.
      AppImpl(MySQL::Forms::Manager^ mgr,
        StrStrStrStrDelegate^ show_file_dialog,
        VoidStrDelegate^ show_status_text,
        BoolStrStrFloatDelegate^ show_progress,
        BoolStrIntStrPtrDelegate^ request_input,
        CanvasViewStringStringIntPtrDelegate^ create_diagram,
        VoidCanvasViewDelegate^ destroy_view,
        VoidCanvasViewDelegate^ switched_view,
        VoidCanvasViewDelegate^ tool_changed,
        IntPtrGRTManagerModuleStrStrGrtListFlagsDelegate^ open_editor,
        VoidIntPtrDelegate^ show_editor,
        VoidIntPtrDelegate^ hide_editor,
        VoidRefreshTypeStringIntPtrDelegate^ refresh_gui,
        VoidBoolDelegate^ lock_gui,
        VoidStrDelegate^ perform_command,
        VoidDelegate^ quit_application,
        StringAppCommandAppViewStringDelegate^ app_command);

      ~AppImpl();

      ::wb::WBFrontendCallbacks* get_callbacks();

      static void init(Manager ^mgr)
      {
        ::mforms::ControlFactory *f= ::mforms::ControlFactory::get_instance();

        DEF_CALLBACK3(void, ::mforms::App*, ::mforms::AppView*, const std::string&, mgr, f->_app_impl, AppImpl, dock_view);
        DEF_CALLBACK2(bool, ::mforms::App*, const std::string&, mgr, f->_app_impl, AppImpl, select_view);
        DEF_CALLBACK2(void, ::mforms::App*, ::mforms::AppView*, mgr, f->_app_impl, AppImpl, undock_view);
        DEF_CALLBACK3(void, ::mforms::App*, ::mforms::AppView*, const std::string&, mgr, f->_app_impl, AppImpl, set_view_title);
        DEF_CALLBACK2(std::string, ::mforms::App*, const std::string&, mgr, f->_app_impl, AppImpl, get_resource_path);
        DEF_CALLBACK2(void, ::mforms::App*, const std::string&, mgr, f->_app_impl, AppImpl, set_status_text);
        DEF_CALLBACK1(std::string, ::mforms::App*, mgr, f->_app_impl, AppImpl, get_bounds);
        DEF_CALLBACK2(int, ::mforms::App*, float, mgr, f->_app_impl, AppImpl, enter_event_loop);
        DEF_CALLBACK2(void, ::mforms::App*, int, mgr, f->_app_impl, AppImpl, exit_event_loop);

        DEF_CALLBACK1(MySQL::Drawing::Color, mforms::SystemColor, mgr, f->_app_impl, AppImpl, get_system_color);
      }
    };
  };
};

#endif // _WF_APP_H_
