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

#include <mforms/view.h>
#include <string>
#include <vector>
#include <map>
#include <boost/signals2.hpp>

namespace mforms
{

  // CellType defines how a cell will be rendered. Cell's type can be changed on fly.
  enum CellType {
    CellInvalid     = 0,
    CellText        = 1,
    CellBool        = 2,
    CellEnum        = 4,
    CellHeader      = 8, // The only collapsible type. This is actually a row type.
    CellGroupHeader = 16 // A simple type with heading style used to group other sibling nodes.
                         // This is actually a row type.
  };

  enum CellsAccess {
    CellsReadable = 0,
    CellsEditable = 1
  };

  enum CellAttr {
    Clear      = 0,
    AlignLeft  = 1,
    AlignRight = 2,
    Underline  = 4
  };

  enum Shade {
    ShadeNone   = 0,
    ShadeFind   = 1,
    ShadeFilter = 2
  };

  enum IconVisibility {
    NotVisible  = 0,
    ShowAlways  = 1,
    ShowOnHover = 2
  };

  enum IconPos {
    IconLeft  = 0,
    IconRight = 1
  };

  class GridPath;
  class Grid;

#ifndef DOXYGEN_SHOULD_SKIP_THIS
#ifndef SWIG
  struct MFORMS_EXPORT GridImplPtrs
  {
    bool (__stdcall *create)(Grid*);
    int (__stdcall *add_column)(Grid*, const std::string& name);
    void (__stdcall *clear)(Grid*);

    int (__stdcall *get_children_count)(Grid*, const GridPath& path);
    bool (__stdcall *is_node_expanded)(Grid*, const GridPath& path);
    void (__stdcall *set_node_expanded)(Grid*, const GridPath& path, const bool expanded);

    void (__stdcall *set_column_width)(Grid*, const int column, const int width);

    // rows additions
    GridPath (__stdcall *append_header)(Grid*, const std::string& gid);
    GridPath (__stdcall *append_row)(Grid*, const GridPath& path);
    GridPath (__stdcall *insert_row)(Grid*, const GridPath& path);
    void           (__stdcall *remove_row)(Grid*, const GridPath& path);

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

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

    // for python code (the impl is inefficient
    bool (__stdcall *set_enum)(Grid*, const GridPath& rid, const int col_id, const std::vector<std::string>& list);
    // ownership of the vector @list will be passed to the GridCell
    bool (__stdcall *set_enum_def)(Grid*, const GridPath& rid, const int col_id, std::vector<std::string>* list);
    // ownership of the char** @list will not be passed to the GridCell
    bool (__stdcall *set_enum_def_c)(Grid*, const GridPath& rid, const int col_id, const char** const list);

    void (__stdcall *scroll_to_row)(Grid*, const GridPath& rid);

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

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

    void (__stdcall *set_action_icon)(Grid*, const GridPath& rid, const int col, const std::string& iconpath, const IconVisibility visible, const IconPos pos);
    void (__stdcall *popup_context_menu)(Grid*);

    // Optional functions are listed below. It is not mandatory to implemented them right now
    bool (__stdcall *set_fg)(Grid*, const GridPath& rid, const int col_id, const double r, const double g, const double b);
    bool (__stdcall *set_bg)(Grid*, const GridPath& rid, const int col_id, const double r, const double g, const double b);

    void (__stdcall *shade)(Grid*, const GridPath& rid, const Shade shade, const int col_id);
    void (__stdcall *unshade)(Grid*, const GridPath& rid, const Shade shade, const int col_id);
    bool (__stdcall *has_shade)(Grid*, const GridPath& rid, const int col_id, const Shade s);
  };
#endif
#endif

  /**
   * GridPath is an artificial thing, much like the bec::NodeId. It contains
   * several indices to address rows. GridPath is used to allow cross platform row addressing.
   */
  class MFORMS_EXPORT GridPath
  {
    public:
      int size() const {return _indices.size();}

      void append(const int i) {_indices.push_back(i);}
      void up() {_indices.pop_back();}
      void next();
      void prev();

      int index(const int i) {return _indices[i];}
      void set_index(const int i, const int index) {_indices[i] = index;}

      #ifndef SWIG
      int& at(const int i) {return _indices.at(i);}
      const int& at(const int i) const {return _indices[i];}

      int& operator[](const int i) {return _indices.at(i);}
      const int& operator[](const int i) const {return _indices[i];}
      #endif

      bool operator==(const GridPath& r) {return _indices == r._indices;}
      bool operator<(const GridPath& r) {return _indices < r._indices;}
      bool operator>(const GridPath& r) {return _indices > r._indices;}
    private:
      std::vector<int> _indices;
  };

  struct GridImplPtrs;

  /**
   * mforms::Grid is a variant of grid control with a tree like first column, where each cell can have
   * an own type out of a defined set of types. Unlike our tree controls where all cells in a column
   * have the same type, grid's cells can have different types within a column
   *
   * TODO: remove this unnecessary limitation.
   * Grid currently should allow two-level addressing, so only topmost nodes can
   * be expanded/collapsed, and nodes of depth == 2 are leaves
   */
  class MFORMS_EXPORT Grid : public mforms::View
  {
    public:
      Grid();

      typedef GridPath  Path;

      int add_column(const std::string& name);
      void clear(); // Clears grid, does not reset columns

      // If path is empty return count of root nodes
      int get_children_count(const Path& path);
      bool is_node_expanded(const Path& path);
      void set_node_expanded(const Path& path, const bool);

      void set_column_width(const int column, const int width);

      // ===== rows additions
      // append_header adds a top most row which will be treated in special way. It must be collapsible
      // also it should collapse/expand on double click. Argument @gid also will serve as a text to 
      // display.
      Path append_header(const std::string& gid);

      // Appends a new row to the @path's first index. So even if I pass a path like [0:1:2], the addition will be
      // done to the 0. Empty path instructs to add to the root.
      Path append_row(const Path& path);
    
      // Inserts a new row after the row at @path. Again, we limit grid depth to 2 levels, so 
      // any path's indices deeper than 2 will be ignored
      Path insert_row(const Path& path);
      void remove_row(const Path& path);

      // set_str_value sets given value to cell, changing cell's type to a CellText
      bool set_str_value(const Path& rid, const int col_id, const std::string& cv, const bool editable);
    
      // set_bool_value sets given value to cell, changing cell's type to a CellBool
      bool set_bool_value(const Path& rid, const int col_id, bool cv, const bool editable);

      #ifndef SWIG
      std::string get_value(const Path& rid, const int col_id, mforms::CellType* type);
      #endif

      // The following 2 functions are specifically designed for Python.
      std::string get_cell_value(const Path& rid, const int col_id);
      mforms::CellType get_cell_type(const Path& rid, const int col_id);

      // passing @col_id == -1 for set_cell_type applies type for all cell in the entire @path row
      void set_cell_type(const Path& path, const int col_id, const mforms::CellType type);
      void set_cell_attr(const Path& path, const int col_id, const int attr); // @attr is a bitmask constructed from CellAttr enum above

      // set_enum_value sets given value to cell, changing cell's type to a CellEnum, also set_enum switches on editable state of the cell
      bool set_enum(const Path& rid, const int col_id, const std::vector<std::string> &list); // for swig & python, Slow!!
   
      #ifndef SWIG
      // ownership of the vector @list will be passed to the GridCell
      bool set_enum_def(const Path& rid, const int col_id, std::vector<std::string>* list);
      // ownership of the char** @list will not be passed to the GridCell
      bool set_enum_def_c(const Path& rid, const int col_id, const char** const list);
      #endif

      void set_row_tag(const Path& path, const std::string& tag);
      std::string get_row_tag(const Path& path);

      // Currently set_row_caption is used to specify text to display for CellGroupHeader cell type
      // all cells in a row must be set_cell_type(path_to_row, -1, mforms::CellGroupHeader) and
      // then set_caption defines text to draw for the row
      void set_row_caption(const Path& path, const std::string& caption);
      std::string get_row_caption(const Path& path);

      // Specifying empty iconpath removes icon
      void set_action_icon(const Path& rid, const int col, const std::string& iconpath, const IconVisibility visible, const IconPos pos);

      // When user right clicks or requests context menu through keyboard this signal is emitted.
      // Signal's handler must request mforms::Menu* using get_context_menu, then the obtained menu
      // is adopted according to the context (row, column) which is passed in signal's arguments.
      // Once menu is setup, popup_context_menu must be called. It is a responsibility of a using code to handle menu
      // actions
      mforms::Menu* get_context_menu() {return &_context_menu;}
      void popup_context_menu();

      void scroll_to_row(const Path& rid);

      // set_fg, set_bg and shading functions are not mandatory to implement
      bool set_fg(const Path& rid, const int col_id, const double r, const double g, const double b);
      bool set_bg(const Path& rid, const int col_id, const double r, const double g, const double b);

      void shade(const Path& rid, const Shade shade, const int col_id = -1);
      void unshade(const Path& rid, const Shade shade, const int col_id = -1);
      bool has_shade(const Path& rid, const int col_id, const Shade s);

      #ifndef SWIG
      // signal_content_action is emitted when user clicks on an action icon
      boost::signals2::signal<void (const Path&, const int, const IconPos)>*  signal_content_action()
      {
        return &_signal_content_action;
      }
      boost::signals2::signal<void (const Path&, const int)>*  signal_content_edited()
      {
        return &_signal_content_edited;
      }
      boost::signals2::signal<void (const Path&, const int)>*  signal_ro_content_clicked()
      {
        return &_signal_ro_content_clicked;
      }
      boost::signals2::signal<void (const Path&, const int)>*  signal_context_menu()
      {
        return &_signal_context_menu;
      }
      #endif

    private:
      boost::signals2::signal<void (const Path&, const int)> _signal_ro_content_clicked;
      boost::signals2::signal<void (const Path&, const int)> _signal_content_edited;
      boost::signals2::signal<void (const Path&, const int, const IconPos)> _signal_content_action;
      boost::signals2::signal<void (const Path&, const int)> _signal_context_menu;

      mforms::Menu _context_menu;

    #ifndef DOXYGEN_SHOULD_SKIP_THIS
    #ifndef SWIG
      GridImplPtrs  *_impl;
    #endif
    #endif
  };

} // namespace mforms

#endif // __MFORMS_GRID_H__
