/* 
 * Copyright (c) 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_GRID_H_
#define _WF_GRID_H_

namespace MySQL {
  namespace Forms {

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

    ref class SortableTreeModel;

    /** A struct to keep cell related values together in one place. */
    private ref struct CellValues
    {
      bool editable;
      String^ text;
      Color background;
      Color foreground;
      List<String^>^ lookupValues;
      mforms::CellType type;
      mforms::CellAttr attributes;

      String^ iconPath;
      Image^ icon;
      mforms::IconPos iconAlignment;
      mforms::IconVisibility iconVisibility;

      CellValues()
      {
        editable = false;
        text = "";
        background = Color::FromKnownColor(KnownColor::Window);
        foreground = Color::FromKnownColor(KnownColor::ControlText);
        lookupValues = nullptr;
        type = mforms::CellInvalid;
        attributes = mforms::Clear;
        iconPath = nullptr;
        icon = nullptr;
        iconAlignment = mforms::IconLeft;
        iconVisibility = mforms::NotVisible;
      }

    };

    /**
     * Due to heavy customization we need to derive an own class.
     */
    private ref class GridTree : public TreeViewAdv
    {
    protected:
      virtual void DrawRow(PaintEventArgs^ e, DrawContext% context, int row,
        System::Drawing::Rectangle rowRect) override;
    };

    /** Enhanced TreeNode for storing individual cell values. */
    private ref class GridNode: Node
    {
    private:
      List<CellValues^> values;

      void ensureIndexes(int index);
    public:
      GridNode();
      ~GridNode();

      property bool IsEditable[int]
      {
        bool get(int index);
        void set(int index, bool flag);
      }

      property String^ Text[int]
      {
        String^ get(int index);
        void set(int index, String^ text);
      }

      property mforms::CellType CellType[int]
      {
        mforms::CellType get(int index);
        void set(int index, mforms::CellType type);
      }

      property mforms::CellAttr CellAttributes[int]
      {
        mforms::CellAttr get(int index);
        void set(int index, mforms::CellAttr attr);
      }

      property Color ForeColor[int]
      {
        Color get(int index);
        void set(int index, Color color);
      }

      property Color BackColor[int]
      {
        Color get(int index);
        void set(int index, Color color);
      }

      property List<String^>^ LookupValues[int]
      {
        List<String^>^ get(int index);
        void set(int index, List<String^>^ _values);
      }

      property String^ IconPath[int]
      {
        String^ get(int index);
        void set(int index, String^ path);
      }

      property mforms::IconPos IconAlignment[int]
      {
        mforms::IconPos get(int index);
        void set(int index, mforms::IconPos alignment);
      }

      property mforms::IconVisibility IconVisiblity[int]
      {
        mforms::IconVisibility get(int index);
        void set(int index, mforms::IconVisibility visiblity);
      }

    };

    /**
     * Node class that applies different styles depending on the cells it is column.
     */
    private ref class MainNodeControl : public NodeTextBox
    {
    private:
      // Since we have to support both the normal text box as well as check boxes we have to
      // merge the implementation of both into one implementation. Since the checkbox code
      // is much smaller we merge this into a NodeTextBox.

      // For check images when running without visual styles.
      Image^ check;
      Image^ uncheck;
      Image^ unknown;

    protected:
      CheckState GetCheckState(TreeNodeAdv^ node);
      CheckState MainNodeControl::GetNewState(CheckState state);
      System::Drawing::Rectangle GetCheckBoxBounds(TreeNodeAdv^ node, DrawContext context);
      void SetCheckState(TreeNodeAdv^ node, CheckState value);

    public:
      MainNodeControl();

      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;
    };

    /**
     * A node control that displays text but allows to edit a cell depending on the cell type
     * and the editable state.
     */
    private ref class EditableNodeControl : public NodeTextBox
    {
    public:
      EditableNodeControl();
    };

    public ref class GridImpl : public ViewImpl
    {
    private:
      // The treeview control will be kept in the ViewImpl's inner variable.
      SortableTreeModel^ _model; // TODO: do we need a sortable model?
      int _current_sort_column;
      bool _can_sort_column;
      SortOrder _current_sort_order;

      TreeNodeAdv^ visual_node_for_path(const mforms::GridPath& path, int limit);
      GridNode^ model_node_for_path(const mforms::GridPath& path, int limit);
      mforms::GridPath path_for_node(Node^ node);

    protected:
      static bool create(mforms::Grid* self);
      static int add_column(mforms::Grid* self, const std::string& name);
      static void clear(mforms::Grid* self);

      static int get_children_count(mforms::Grid* self, const mforms::GridPath& path);
      static bool is_node_expanded(mforms::Grid* self, const mforms::GridPath& path);
      static void set_node_expanded(mforms::Grid* self, const mforms::GridPath& path, const bool expanded);

      static void set_column_width(mforms::Grid* self, const int column, const int width);

      static mforms::GridPath append_header(mforms::Grid* self, const std::string& gid);
      static mforms::GridPath append_row(mforms::Grid* self, const mforms::GridPath& path);
      static mforms::GridPath insert_row(mforms::Grid* self, const mforms::GridPath& path);
      static void remove_row(mforms::Grid* self, const mforms::GridPath& path);

      static bool set_str_value(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const std::string& cv, const bool editable);
      static bool set_bool_value(mforms::Grid* self, const mforms::GridPath& path, const int col_id, bool cv, const bool editable);
      static std::string get_value(mforms::Grid* self, const mforms::GridPath& path, const int col_id, mforms::CellType* type);

      static void set_cell_type(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const mforms::CellType type);
      static void set_cell_attr(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const int attr);

      static bool set_fg(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const double r, const double g, const double b);
      static bool set_bg(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const double r, const double g, const double b);

      static bool set_enum(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const std::vector<std::string>& list);
      // ownership of the vector @list will be passed to the GridCell
      static bool set_enum_def(mforms::Grid* self, const mforms::GridPath& path, const int col_id, std::vector<std::string>* list);
      // ownership of the char** @list will not be passed to the GridCell
      static bool set_enum_def_c(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const char** const list);

      static void shade(mforms::Grid* self, const mforms::GridPath& path, const mforms::Shade shade, const int col_id);
      static void unshade(mforms::Grid* self, const mforms::GridPath& path, const mforms::Shade shade, const int col_id);
      static bool has_shade(mforms::Grid* self, const mforms::GridPath& path, const int col_id, const mforms::Shade s);
      static void scroll_to_row(mforms::Grid* self, const mforms::GridPath& path);

      static void set_row_tag(mforms::Grid* self, const mforms::GridPath& path, const std::string& tag);
      static std::string get_row_tag(mforms::Grid* self, const mforms::GridPath& path);

      static void set_row_caption(mforms::Grid* self, const mforms::GridPath& path, const std::string& caption);
      static std::string get_row_caption(mforms::Grid* self, const mforms::GridPath& path);

      static void set_action_icon(mforms::Grid* self, const mforms::GridPath& path, const int col, const std::string& iconpath, const mforms::IconVisibility visible, const mforms::IconPos pos);
      static void popup_context_menu(mforms::Grid* self);

      // Event handlers.
      void TreeValueNeeded(System::Object^ sender, NodeControlValueEventArgs^ e);
      void TreeValuePushed(System::Object^ sender, NodeControlValueEventArgs^ e);
      //void SelectionChanged(System::Object^ sender, EventArgs^ e);
      //void NodeActivated(System::Object^ sender, TreeNodeAdvMouseEventArgs^ args);
      //void OnMouseDown(System::Object^ sender, MouseEventArgs^ e);
      //void OnColumnClicked(System::Object^ sender, TreeColumnEventArgs^ e);
    public:
      GridImpl(mforms::Grid *self);

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

        DEF_CALLBACK1(bool, mforms::Grid*, mgr, f->_grid_impl, GridImpl, create);
        DEF_CALLBACK2(int, mforms::Grid*, const std::string&, mgr, f->_grid_impl, GridImpl, add_column);
        DEF_CALLBACK1(void, mforms::Grid*, mgr, f->_grid_impl, GridImpl, clear);
        DEF_CALLBACK2(int, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, get_children_count);
        DEF_CALLBACK2(bool, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, is_node_expanded);
        DEF_CALLBACK3(void, mforms::Grid*, const mforms::GridPath&, const bool, mgr, f->_grid_impl, GridImpl, set_node_expanded);
        DEF_CALLBACK3(void, mforms::Grid*, const int, const int, mgr, f->_grid_impl, GridImpl, set_column_width);
        DEF_CALLBACK2(mforms::GridPath, mforms::Grid*, const std::string&, mgr, f->_grid_impl, GridImpl, append_header);
        DEF_CALLBACK2(mforms::GridPath, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, append_row);
        DEF_CALLBACK2(mforms::GridPath, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, insert_row);
        DEF_CALLBACK2(void, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, remove_row);
        DEF_CALLBACK5(bool, mforms::Grid*, const mforms::GridPath&, const int, const std::string&, const bool, mgr, f->_grid_impl, GridImpl, set_str_value);
        DEF_CALLBACK5(bool, mforms::Grid*, const mforms::GridPath&, const int, bool, const bool, mgr, f->_grid_impl, GridImpl, set_bool_value);
        DEF_CALLBACK4(std::string, mforms::Grid*, const mforms::GridPath&, const int, mforms::CellType*, mgr, f->_grid_impl, GridImpl, get_value);
        DEF_CALLBACK4(void, mforms::Grid*, const mforms::GridPath&, const int, const mforms::CellType, mgr, f->_grid_impl, GridImpl, set_cell_type);
        DEF_CALLBACK4(void, mforms::Grid*, const mforms::GridPath&, const int, const int, mgr, f->_grid_impl, GridImpl, set_cell_attr);
        DEF_CALLBACK6(bool, mforms::Grid*, const mforms::GridPath&, const int, const double, const double, const double, mgr, f->_grid_impl, GridImpl, set_fg);
        DEF_CALLBACK6(bool, mforms::Grid*, const mforms::GridPath&, const int, const double, const double, const double, mgr, f->_grid_impl, GridImpl, set_bg);
        DEF_CALLBACK4(bool, mforms::Grid*, const mforms::GridPath&, const int, const std::vector<std::string>&, mgr, f->_grid_impl, GridImpl, set_enum);
        DEF_CALLBACK4(bool, mforms::Grid*, const mforms::GridPath&, const int, std::vector<std::string>*, mgr, f->_grid_impl, GridImpl, set_enum_def);
        DEF_CALLBACK4(bool, mforms::Grid*, const mforms::GridPath&, const int, const char** const, mgr, f->_grid_impl, GridImpl, set_enum_def_c);
        DEF_CALLBACK4(void, mforms::Grid*, const mforms::GridPath&, const mforms::Shade, const int, mgr, f->_grid_impl, GridImpl, shade);
        DEF_CALLBACK4(void, mforms::Grid*, const mforms::GridPath&, const mforms::Shade, const int, mgr, f->_grid_impl, GridImpl, unshade);
        DEF_CALLBACK4(bool, mforms::Grid*, const mforms::GridPath&, const int, const mforms::Shade, mgr, f->_grid_impl, GridImpl, has_shade);
        DEF_CALLBACK2(void, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, scroll_to_row);
        DEF_CALLBACK3(void, mforms::Grid*, const mforms::GridPath&, const std::string&, mgr, f->_grid_impl, GridImpl, set_row_tag);
        DEF_CALLBACK2(std::string, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, get_row_tag);
        DEF_CALLBACK3(void, mforms::Grid*, const mforms::GridPath&, const std::string&, mgr, f->_grid_impl, GridImpl, set_row_caption);
        DEF_CALLBACK2(std::string, mforms::Grid*, const mforms::GridPath&, mgr, f->_grid_impl, GridImpl, get_row_caption);
        DEF_CALLBACK6(void, mforms::Grid*, const mforms::GridPath&, const int, const std::string&, const mforms::IconVisibility, const mforms::IconPos, mgr, f->_grid_impl, GridImpl, set_action_icon);
        DEF_CALLBACK1(void, mforms::Grid*, mgr, f->_grid_impl, GridImpl, popup_context_menu);
      }
    };

  };
};


#endif _WF_GRID_H_
