//+----------------------------------------------------------------------------+
//| Description:  Magic Set Editor - Program to make Magic (tm) cards          |
//| Copyright:    (C) Twan van Laarhoven and the other MSE developers          |
//| License:      GNU General Public License 2 or later (see file COPYING)     |
//+----------------------------------------------------------------------------+

#pragma once

// ----------------------------------------------------------------------------- : Includes

#include <util/prec.hpp>
#include <util/real_point.hpp>

// ----------------------------------------------------------------------------- : Events

DECLARE_LOCAL_EVENT_TYPE(EVENT_GALLERY_SELECT,   <not used>)
DECLARE_LOCAL_EVENT_TYPE(EVENT_GALLERY_ACTIVATE, <not used>)

/// Handle EVENT_GALLERY_SELECT events
#define EVT_GALLERY_SELECT(  id, handler) EVT_COMMAND(id, EVENT_GALLERY_SELECT,   handler)
/// Handle EVENT_GALLERY_ACTIVATE events
#define EVT_GALLERY_ACTIVATE(id, handler) EVT_COMMAND(id, EVENT_GALLERY_ACTIVATE, handler)

// ----------------------------------------------------------------------------- : GalleryList

/// A list of items with custom drawing
/** A derived class should implement the abstract members to determine how the items look.
 */
class GalleryList : public wxPanel {
public:
  GalleryList(Window* parent, int id, int direction = wxHORIZONTAL, bool always_focused = true);
  
  /// Select the given column
  void selectSubColumn(size_t subcol);
  /// Select the given item in the given column (or in the active column)
  void select(size_t item, size_t subcol = NO_SELECTION, bool event = true);
  /// Is there an item selected?
  inline bool hasSelection(size_t subcol = 0) const { return subcolumns[subcol].selection < itemCount(); }
  /// Get the id of the selected item, throws if there is no selection
  inline size_t getSelectionId(size_t subcol = 0) const {
    if (!hasSelection(subcol)) throw Error(_("No selection"));
    return subcolumns[subcol].selection;
  }
  /// Is the given item selected?
  inline bool isSelected(size_t item, size_t subcol = 0) const {
    return subcol < subcolumns.size() && subcolumns[subcol].selection == item;
  }
  
  /// Redraw only the selected items
  void RefreshSelection();
  
protected:
  static const size_t NO_SELECTION = (size_t)-1;
  size_t active_subcolumn;  ///< The active subcolumn
  wxSize item_size;         ///< The total size of a single item (over all columns)
  int direction;            ///< Direction of the list, can be wxHORIZONTAL or wxVERTICAL
  size_t column_count;      ///< Number of major level columns (if vertical) or rows (if horizontal)
  bool always_focused;      ///< Always draw as if focused
  
  /// Redraw the list after changing the selection or the number of items
  void update();
  
  /// Return how many items there are in the list
  virtual size_t itemCount() const = 0;
  /// Draw an item
  virtual void drawItem(DC& dc, int x, int y, size_t item) = 0;
  /// How 'salient' should the selection in the given subcolumn be?
  virtual double subcolumnActivity(size_t col) const { return 0.7; }
  
  /// Filter calls to select, or apply some extra operaions
  virtual void onSelect(size_t item, size_t col, bool& changes) {}
  
  /// Return the desired size of control
  wxSize DoGetBestSize() const override;
  
  /// Information on the subcolumns. These are columns inside items
  struct SubColumn {
    wxPoint offset;
    wxSize  size;
    bool    can_select;
    size_t  selection;
  };
  vector<SubColumn> subcolumns;
  
private:
  DECLARE_EVENT_TABLE();
  
  void onLeftDown  (wxMouseEvent& ev);
  void onLeftDClick(wxMouseEvent& ev);
  void onMouseWheel(wxMouseEvent& ev);
  void onChar(wxKeyEvent& ev);
  void onFocus(wxFocusEvent&);
  void onPaint(wxPaintEvent&);
  void onSize(wxSizeEvent&);
  void onScroll(wxScrollWinEvent&);
  void OnDraw(DC& dc);
  
  /// Find the item corresponding to the given location
  size_t findItem(const wxMouseEvent&) const;
  /// Find the coordinates of an item
  wxPoint itemPos(size_t item) const;
  
  /// Scroll to the given position (note: 'top' can also mean 'left')
  void scrollTo(int top, bool update_scrollbar = true);
  /// Update the scrollbar(s)
  void updateScrollbar();
  /// Redraw just a single item
  void RefreshItem(size_t item);
  
  /// First visible pixel position
  int visible_start;
  /// First no-longer-visible pixel position
  inline int visibleEnd() const {
    return visible_start + mainSize(GetClientSize());
  }
  /// Pixel position of an item
  inline int itemStart(size_t item) const {
    return (int)(item / column_count) * (mainSize(item_size) + SPACING);
  }
  inline int itemEnd(size_t item) const {
    return (int)(item / column_count + 1) * (mainSize(item_size) + SPACING) + MARGIN;
  }
  /// Main component of a size (i.e. in the direction of this list)
  inline int mainSize(wxSize s) const {
    return direction == wxHORIZONTAL ? s.x : s.y;
  }

public:
  typedef SubColumn SubColumn_for_typeof;
protected:
  /// Send an event
  void sendEvent(WXTYPE type);

  static const int MARGIN = 1; // margin between items (excluding border)
  static const int BORDER = 0; // border around items
  static const int SPACING = MARGIN + 2*BORDER; // distance between items
};

