/* 
 * Copyright (c) 2009, 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_TREEVIEW_H_
#define _WF_TREEVIEW_H_

#include "mforms/treeview.h"
#include "wf_view.h"

using namespace std;

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

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

namespace MySQL {
  namespace Forms {

    /**
     * A simple helper class to store column specific text in a node.
     */
    public ref class TreeNode: Node
    {
    private:
      List<String^> _text;
      TreeViewAdv^ _tree;
    public:
      TreeNode(TreeViewAdv^ tree);

      String ^tag;

      TreeViewAdv^ GetTree() { return _tree; };

      property String^ Text[int]
      {
        String^ get(int index)
        {
          if (index < 0 || index >= _text.Count)
            return "";
          return _text[index] == nullptr ? "" : _text[index];
        }
        void set(int index, String^ newText)
        {
          while (index >= _text.Count)
            _text.Add(nullptr);
          _text[index] = newText;
        }
      }
    };

    /**
     * The class that does the actual text comparison for sorting.
     */
    private ref class ColumnComparer : public Collections::IComparer
    {
    private:
      int _column;
      SortOrder _direction;
      CaseInsensitiveComparer^ _inner;
    public:
      ColumnComparer(int column, SortOrder direction);

      virtual int Compare(System::Object^ object1, System::Object^ object2);
    };

    /**
     * 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::IComparer^ _comparer;

      void SortNodes(Collection<Node^>^ nodes);

    public:
      property Collections::IComparer^ Comparer
      {
        Collections::IComparer^ get()
        {
          return _comparer;
        }
        void set(Collections::IComparer^ value)
        { 
          _comparer = value;
          if (_comparer != nullptr)
            Sort();
        }
      }

      void Sort();
    };

    /**
     * Wrapper for a native .NET treeview control. We use the TreeViewAdv control as we need 
     * multicolumn support.
     */
    public ref class TreeViewImpl : 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;
      SortOrder _current_sort_order;
    protected:
      static bool create(::mforms::TreeView *self, ::mforms::TreeOptions options);
      static int add_column(::mforms::TreeView *self, ::mforms::TreeColumnType type, const string &name, 
        int initial_width, bool editable);
      static void end_columns(::mforms::TreeView *self);
      
      static void clear_rows(::mforms::TreeView *self);

      static void delete_row(::mforms::TreeView *self, int row);
      static int add_row(::mforms::TreeView *self);
      static int get_selected(::mforms::TreeView *self);
      static void set_selected(::mforms::TreeView *self, const int idx);

      static void set_allow_sorting(mforms::TreeView *self, bool flag);
      static int count(::mforms::TreeView *self);

      static void set_row_tag(::mforms::TreeView *self, int row, const string &value);
      static void set_string(::mforms::TreeView *self, int row, int column, const string &value);
      static void set_int(::mforms::TreeView *self, int row, int column, int value);
      static void set_long(::mforms::TreeView *self, int row, int column, boost::int64_t value);
      static void set_check(::mforms::TreeView *self, int row, int column, bool value);

      static string get_row_tag(::mforms::TreeView *self, int row);
      static string get_string(::mforms::TreeView *self, int row, int column);
      static int get_int(::mforms::TreeView *self, int row, int column);
      static boost::int64_t get_long(::mforms::TreeView *self, int row, int column);
      static bool get_check(::mforms::TreeView *self, int row, int column);

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

      // 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);
    public:
      TreeViewImpl(::mforms::TreeView *self);

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

      void delete_row(int row);
      int add_row();
      int get_selected();
      void set_selected(const int idx);

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

      void set_row_tag(int row, const string &value);
      void set_string(int row, int column, const string &value);
      void set_int(int row, int column, int value);
      void set_long(int row, int column, boost::int64_t value);
      void set_check(int row, int column, bool value);

      string get_row_tag(int row);
      string get_string(int row, int column);
      int get_int(int row, int column);
      boost::int64_t get_long(int row, int column);
      bool get_check(int row, int column);

      void freeze_refresh(bool flag);

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

        DEF_CALLBACK2(bool, ::mforms::TreeView*, ::mforms::TreeOptions, mgr, f->_treeview_impl, TreeViewImpl, create);
        DEF_CALLBACK5(int, ::mforms::TreeView*, ::mforms::TreeColumnType, const string&, int, bool, mgr, 
          f->_treeview_impl, TreeViewImpl, add_column);
        DEF_CALLBACK1(void, ::mforms::TreeView*, mgr, f->_treeview_impl, TreeViewImpl, end_columns);
        DEF_CALLBACK1(void, ::mforms::TreeView*, mgr, f->_treeview_impl, TreeViewImpl, clear_rows);
        DEF_CALLBACK2(void, ::mforms::TreeView*, int, mgr, f->_treeview_impl, TreeViewImpl, delete_row);
        DEF_CALLBACK1(int, ::mforms::TreeView*, mgr, f->_treeview_impl, TreeViewImpl, add_row);
        DEF_CALLBACK1(int, ::mforms::TreeView*, mgr, f->_treeview_impl, TreeViewImpl, get_selected);
        DEF_CALLBACK2(void, ::mforms::TreeView*, const int, mgr, f->_treeview_impl, TreeViewImpl, set_selected);
        DEF_CALLBACK2(void, ::mforms::TreeView*, bool, mgr, f->_treeview_impl, TreeViewImpl, set_allow_sorting);
        DEF_CALLBACK1(int, ::mforms::TreeView*, mgr, f->_treeview_impl, TreeViewImpl, count);
        DEF_CALLBACK3(void, ::mforms::TreeView*, int, const string&, mgr, f->_treeview_impl, TreeViewImpl, set_row_tag);
        DEF_CALLBACK4(void, ::mforms::TreeView*, int, int, const string&, mgr, f->_treeview_impl, TreeViewImpl, set_string);
        DEF_CALLBACK4(void, ::mforms::TreeView*, int, int, int, mgr, f->_treeview_impl, TreeViewImpl, set_int);
        DEF_CALLBACK4(void, ::mforms::TreeView*, int, int, boost::int64_t, mgr, f->_treeview_impl, TreeViewImpl, set_long);
        DEF_CALLBACK4(void, ::mforms::TreeView*, int, int, bool, mgr, f->_treeview_impl, TreeViewImpl, set_check);
        DEF_CALLBACK2(string, ::mforms::TreeView*, int, mgr, f->_treeview_impl, TreeViewImpl, get_row_tag);
        DEF_CALLBACK3(string, ::mforms::TreeView*, int, int, mgr, f->_treeview_impl, TreeViewImpl, get_string);
        DEF_CALLBACK3(int, ::mforms::TreeView*, int, int, mgr, f->_treeview_impl, TreeViewImpl, get_int);
        DEF_CALLBACK3(boost::int64_t, ::mforms::TreeView*, int, int, mgr, f->_treeview_impl, TreeViewImpl, get_long);
        DEF_CALLBACK3(bool, ::mforms::TreeView*, int, int, mgr, f->_treeview_impl, TreeViewImpl, get_check);
        DEF_CALLBACK2(void, ::mforms::TreeView*, bool, mgr, f->_treeview_impl, TreeViewImpl, freeze_refresh);
      }
    };
  }
}

#endif // _WF_TREEVIEW_H_