/* 
 * Copyright (c) 2012, 2013, 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
 */

#pragma once

using namespace std;

using namespace System;
using namespace Windows::Forms;
using namespace System::Collections::ObjectModel;

using namespace Aga::Controls::Tree;
using namespace Aga::Controls::Tree::NodeControls;

namespace MySQL {
  namespace Forms {
    
    // We need a separate managed type for columns type.
    enum class ManagedTreeColumnType {
      MStringColumnType = mforms::StringColumnType,
      MIntegerColumnType = mforms::IntegerColumnType,
      MLongIntegerColumnType = mforms::LongIntegerColumnType,
      MCheckColumnType = mforms::CheckColumnType,
      MIconColumnType = mforms::IconColumnType,
      MIconStringColumnType = mforms::IconStringColumnType
    };

    /**
     * A pretty standard tree model which allows to sort nodes.
     * Note: the way sorting is handled is completely unoptimized and hence not recommended
     *       for larger node counts.
     */
    private ref class SortableTreeModel : Aga::Controls::Tree::TreeModel
    {
    private:
      Collections::Generic::IComparer<Node^>^ _comparer;

    public:
      property Collections::Generic::IComparer<Node^>^ Comparer
      {
        Collections::Generic::IComparer<Node^>^ get()
        {
          return _comparer;
        }
        void set(Collections::Generic::IComparer<Node^>^ value)
        { 
          _comparer = value;
          OnStructureChanged(gcnew TreePathEventArgs());
        }
      }

      virtual Collections::IEnumerable^ GetChildren(TreePath ^treePath) override;

      void Resort();
    };

    private ref class AttributedNodeText : public NodeTextBox
    {
    protected:
      virtual void OnDrawText(DrawEventArgs^ args) override;
    };

    /**
     * Helper class to show our own expand icons.
     */
    private ref class TriangleNodeControl : public NodeControl
    {
    private:
      System::Drawing::Bitmap^ _expanded_icon;
      System::Drawing::Bitmap^ _collapsed_icon;
      System::Drawing::Size _size; 
    public:
      TriangleNodeControl();

      virtual System::Drawing::Size MeasureSize(TreeNodeAdv^ node, DrawContext context) override;
      virtual void Draw(TreeNodeAdv^ node, DrawContext context) override;
      virtual void MouseDown(TreeNodeAdvMouseEventArgs^ args) override;
      virtual void MouseDoubleClick(TreeNodeAdvMouseEventArgs^ args) override;
    };


    ref class TreeViewNode;

    /**
     * Wrapper for a native .NET treeview control. We use the TreeViewAdv control as we need 
     * multicolumn support.
     */
    public ref class TreeNodeViewImpl : public ViewImpl
    {
    private:
      // The treeview control will be kept in the ViewImpl's inner variable.
      SortableTreeModel^ _model;
      int _current_sort_column;
      bool _can_sort_column;
      bool _flat_list;
      int _freeze_count;
      SortOrder _current_sort_order;
      List<ManagedTreeColumnType> _column_types;

      Dictionary<String^, TreeViewNode^>^ _tag_map;
    protected:
      static bool create(mforms::TreeNodeView *self, mforms::TreeOptions options);
      static int add_column(mforms::TreeNodeView *self, mforms::TreeColumnType type, const std::string &name, 
        int initial_width, bool editable);
      static void end_columns(mforms::TreeNodeView *self);
      
      static void clear(mforms::TreeNodeView *self);

      static mforms::TreeSelectionMode get_selection_mode(mforms::TreeNodeView *self);
      static void set_selection_mode(mforms::TreeNodeView *self, mforms::TreeSelectionMode mode);
      static std::list<mforms::TreeNodeRef> get_selection(mforms::TreeNodeView *self);
      static mforms::TreeNodeRef get_selected_node(mforms::TreeNodeView *self);
      static void clear_selection(mforms::TreeNodeView *self);
      static void set_selected(mforms::TreeNodeView *self, mforms::TreeNodeRef node, bool flag);

      static void set_allow_sorting(mforms::TreeNodeView *self, bool flag);
      static void set_row_height(mforms::TreeNodeView *self, int h);

      static void freeze_refresh(mforms::TreeNodeView *self, bool flag);

      static mforms::TreeNodeRef root_node(mforms::TreeNodeView *self);

      static mforms::TreeNodeRef node_at_row(mforms::TreeNodeView *self, int row);
      static int row_for_node(mforms::TreeNodeView *self, mforms::TreeNodeRef node);
      static mforms::TreeNodeRef node_with_tag(mforms::TreeNodeView *self, const std::string &tag);

      static void set_column_visible(mforms::TreeNodeView *self, int column, bool flag);
      static bool get_column_visible(mforms::TreeNodeView *self, int column);

      // Event handlers.
      static void TreeValueNeeded(System::Object^ sender, NodeControlValueEventArgs^ e);
      static void TreeValuePushed(System::Object^ sender, NodeControlValueEventArgs^ e);
      static void SelectionChanged(System::Object^ sender, EventArgs^ e);
      static void NodeActivated(System::Object^ sender, TreeNodeAdvMouseEventArgs^ args);
      static void OnMouseDown(System::Object^ sender, MouseEventArgs^ e);

      void OnColumnClicked(System::Object^ sender, TreeColumnEventArgs^ e);
      void Expanded(System::Object^ sender, TreeViewAdvEventArgs^ e);
      void Collapsed(System::Object^ sender, TreeViewAdvEventArgs^ e);
      void OnBeforeNodeDrawing(System::Object^ sender, TreeViewAdvDrawRowEventArgs^ e);
    public:
      TreeNodeViewImpl(mforms::TreeNodeView *self, bool use_tag_index);
      virtual ~TreeNodeViewImpl();

      void set_row_height(int h);

      int add_column(mforms::TreeColumnType type, const std::string &name, int initial_width, bool editable);
      void end_columns();
      
      void clear();

      mforms::TreeSelectionMode get_selection_mode();
      void set_selection_mode(mforms::TreeSelectionMode mode);

      void clear_selection();
      std::list<mforms::TreeNodeRef> get_selection();
      mforms::TreeNodeRef get_selected_node();
      void set_selected(mforms::TreeNodeRef node, bool flag);

      void allow_column_sorting(bool flag);
      int current_sort_column();

      void freeze_refresh(bool flag);
      bool is_frozen();

      mforms::TreeNodeRef root_node();
      
      mforms::TreeNodeRef node_at_row(int row);
      int row_for_node(mforms::TreeNodeRef node);
      mforms::TreeNodeRef node_with_tag(const std::string &tag);

      void set_column_visible(int column, bool flag);
      bool is_column_visible(int column);

      void process_mapping(TreeViewNode^ node, const std::string &tag); // Internal function.

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

        DEF_CALLBACK2(bool, mforms::TreeNodeView*, mforms::TreeOptions, mgr, f->_treenodeview_impl, TreeNodeViewImpl, create);
        DEF_CALLBACK5(int, mforms::TreeNodeView*, mforms::TreeColumnType, const std::string&, int, bool, mgr, 
          f->_treenodeview_impl, TreeNodeViewImpl, add_column);
        DEF_CALLBACK1(void, mforms::TreeNodeView*, mgr, f->_treenodeview_impl, TreeNodeViewImpl, end_columns);
        DEF_CALLBACK1(void, mforms::TreeNodeView*, mgr, f->_treenodeview_impl, TreeNodeViewImpl, clear);
        DEF_CALLBACK1(void, mforms::TreeNodeView*, mgr, f->_treenodeview_impl, TreeNodeViewImpl, clear_selection);
        DEF_CALLBACK1(std::list<mforms::TreeNodeRef>, mforms::TreeNodeView*, mgr, f->_treenodeview_impl, TreeNodeViewImpl, get_selection);
        DEF_CALLBACK1(mforms::TreeNodeRef, mforms::TreeNodeView*, mgr, f->_treenodeview_impl, TreeNodeViewImpl, get_selected_node);
        DEF_CALLBACK3(void, mforms::TreeNodeView*, mforms::TreeNodeRef, bool, mgr, f->_treenodeview_impl, TreeNodeViewImpl, set_selected);
        DEF_CALLBACK2(void, mforms::TreeNodeView*, bool, mgr, f->_treenodeview_impl, TreeNodeViewImpl, set_allow_sorting);
        DEF_CALLBACK2(void, mforms::TreeNodeView*, int, mgr, f->_treenodeview_impl, TreeNodeViewImpl, set_row_height);
        DEF_CALLBACK2(void, mforms::TreeNodeView*, bool, mgr, f->_treenodeview_impl, TreeNodeViewImpl, freeze_refresh);
        DEF_CALLBACK1(mforms::TreeNodeRef, mforms::TreeNodeView*, mgr, f->_treenodeview_impl, TreeNodeViewImpl, root_node);
        DEF_CALLBACK2(int, mforms::TreeNodeView*, mforms::TreeNodeRef, mgr, f->_treenodeview_impl, TreeNodeViewImpl, row_for_node);
        DEF_CALLBACK2(mforms::TreeNodeRef, mforms::TreeNodeView*, int, mgr, f->_treenodeview_impl, TreeNodeViewImpl, node_at_row);
        DEF_CALLBACK2(void, mforms::TreeNodeView *, mforms::TreeSelectionMode, mgr, f->_treenodeview_impl, TreeNodeViewImpl, set_selection_mode);
        DEF_CALLBACK1(mforms::TreeSelectionMode, mforms::TreeNodeView *, mgr, f->_treenodeview_impl, TreeNodeViewImpl, get_selection_mode);
        DEF_CALLBACK2(mforms::TreeNodeRef, mforms::TreeNodeView *, const std::string&, mgr, f->_treenodeview_impl, TreeNodeViewImpl, node_with_tag);
        DEF_CALLBACK3(void, mforms::TreeNodeView*, int, bool, mgr, f->_treenodeview_impl, TreeNodeViewImpl, set_column_visible);
        DEF_CALLBACK2(bool, mforms::TreeNodeView*, int, mgr, f->_treenodeview_impl, TreeNodeViewImpl, get_column_visible);
      }
    };
  }
}

