//+----------------------------------------------------------------------------+
//| 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)     |
//+----------------------------------------------------------------------------+

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

#include <util/prec.hpp>
#include <gui/auto_replace_window.hpp>
#include <gui/control/item_list.hpp>
#include <gui/util.hpp>
#include <data/settings.hpp>
#include <data/game.hpp>
#include <data/word_list.hpp>
#include <gfx/gfx.hpp>
#include <util/window_id.hpp>
#include <util/error.hpp>

// ----------------------------------------------------------------------------- : AutoReplaceList

DECLARE_EVENT_TYPE(EVENT_ITEM_SELECT, <not used>)
DEFINE_EVENT_TYPE(EVENT_ITEM_SELECT);

class AutoReplaceList : public ItemList {
public:
  AutoReplaceList(Window* parent, int id, const Game& game);

  /// The items
  vector<AutoReplaceP> items;
  /// Settings we are edditing
  const Game& game;
  GameSettings& gs;

  /// The current item
  inline AutoReplaceP getSelected() const { return static_pointer_cast<AutoReplace>(selected_item); }

  /// Add an item
  void addItem(const AutoReplaceP& item);
  /// Remove the selected item
  void removeSelected();

  /// Reset the list to the default
  void reset();

  using ItemList::refreshList;

protected:
  /// Get all items
  void getItems(vector<VoidP>& out) const override;
  /// Return the AutoReplace at the given position in the sorted list
  inline AutoReplaceP getAR(long pos) const { return static_pointer_cast<AutoReplace>(getItem(pos)); }

  /// Send an 'item selected' event for the currently selected item (selected_item)
  void sendEvent() override;
  /// Compare items
  bool compareItems(void* a, void* b) const override;
  bool mustSort() const override { return true; }

  /// Get the text of an item in a specific column
  /** Overrides a function from wxListCtrl */
  String OnGetItemText (long pos, long col) const override;
  /// Get the image of an item, by default no image is used
  /** Overrides a function from wxListCtrl */
  int OnGetItemImage(long pos) const override;
  /// Get the color for an item
  wxListItemAttr* OnGetItemAttr(long pos) const override;

  mutable wxListItemAttr item_attr; // for OnGetItemAttr
};

AutoReplaceList::AutoReplaceList(Window* parent, int id, const Game& game)
  : ItemList(parent, id)
  , game(game)
  , gs(settings.gameSettingsFor(game))
{
  // clone items
  FOR_EACH_CONST(ar, gs.auto_replaces) {
    items.push_back(ar->clone());
  }
  // Add columns
  InsertColumn(0, _(""),                   wxLIST_FORMAT_LEFT,    0); // dummy, prevent the image from taking up space
  InsertColumn(1, _LABEL_("auto match"),   wxLIST_FORMAT_LEFT,  100);
  InsertColumn(2, _LABEL_("auto replace"), wxLIST_FORMAT_LEFT,  200);
  // grey for disabled items
  item_attr.SetTextColour(lerp(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW),
                               wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),0.5));
  // init list
  refreshList();
  sortBy(1,true);
}

void AutoReplaceList::addItem(const AutoReplaceP& item) {
  items.push_back(item);
  refreshList();
  selectItem(item, true, true);
}

void AutoReplaceList::removeSelected() {
  for (size_t i = 0 ; i < items.size() ; ++i) {
    if (items[i] == selected_item) {
      items.erase(items.begin() + i);
      // select next
      refreshList();
      selectItem(items.empty() ? VoidP() : static_pointer_cast<IntrusivePtrVirtualBase>(items[min(i, items.size())]),
                 true, true);
      return;
    }
  }
}

void AutoReplaceList::reset() {
  // reset to list from game
  items.clear();
  FOR_EACH_CONST(ar, game.auto_replaces) {
    items.push_back(ar->clone());
  }
  refreshList();
}

void AutoReplaceList::getItems(vector<VoidP>& out) const {
  FOR_EACH_CONST(i, items) out.push_back(i);
}

void AutoReplaceList::sendEvent() {
  wxCommandEvent ev(EVENT_ITEM_SELECT, GetId());
  ProcessEvent(ev);
}

bool AutoReplaceList::compareItems(void* ap, void* bp) const {
  AutoReplace& a = *static_cast<AutoReplace*>(ap);
  AutoReplace& b = *static_cast<AutoReplace*>(bp);
  switch (sort_by_column) {
    case 2:  return a.replace < b.replace;
    default: return a.match   < b.match;
  }
}

String AutoReplaceList::OnGetItemText (long pos, long col) const {
  AutoReplaceP ar = getAR(pos);
  if (col == 0) return ar->match;
  if (col == 1) return ar->match;
  if (col == 2) return ar->replace;
  throw InternalError(_("too many columns"));
}

int AutoReplaceList::OnGetItemImage(long pos) const {
  return -1;
}

wxListItemAttr* AutoReplaceList::OnGetItemAttr(long pos) const {
  // grey disabled keywords
  return getAR(pos)->enabled ? nullptr : &item_attr;
}

// ----------------------------------------------------------------------------- : AutoReplaceWindow

AutoReplaceWindow::AutoReplaceWindow(Window* parent, const Game& game)
  : wxDialog(parent, wxID_ANY, _TITLE_("auto replaces"))
  , in_event(false)
{
  // Create controls
  list       = new AutoReplaceList(this, wxID_ANY, game);
  match      = new wxTextCtrl(this, ID_ITEM_VALUE);
  replace    = new wxTextCtrl(this, ID_ITEM_VALUE);
  enabled    = new wxCheckBox(this, ID_ITEM_VALUE, _BUTTON_("enabled"));
  whole_word = new wxCheckBox(this, ID_ITEM_VALUE, _BUTTON_("whole word"));
  use_auto_replace   = new wxCheckBox(this, ID_USE_AUTO_REPLACE, _BUTTON_("use auto replace"));
  add                = new wxButton(this, ID_ADD_ITEM,    _BUTTON_("add item"));
  remove             = new wxButton(this, ID_REMOVE_ITEM, _BUTTON_("remove item"));
  wxButton* defaults = new wxButton(this, ID_DEFAULTS,    _BUTTON_("defaults"));
  matchL = new wxStaticText(this, wxID_ANY, _LABEL_("auto match"));
  replaceL = new wxStaticText(this, wxID_ANY, _LABEL_("auto replace"));
  // Create sizer
  wxSizer* s = new wxBoxSizer(wxVERTICAL);
    // enabled?
    s->Add(use_auto_replace, 0, wxALL & ~wxBOTTOM, 8);
    // list
    s->Add(list, 1, wxEXPAND | (wxALL & ~wxBOTTOM), 8);
    s->AddSpacer(4);
    wxSizer* s2 = new wxBoxSizer(wxHORIZONTAL);
      s2->Add(add, 0, wxRIGHT, 4);
      s2->Add(remove);
    s->Add(s2, 0, wxALIGN_RIGHT | (wxALL & ~wxTOP & ~wxBOTTOM), 8);
    // values
    wxFlexGridSizer* s3 = new wxFlexGridSizer(2);
      s3->AddGrowableCol(1);
      s3->Add(matchL,   0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
      s3->Add(match,   1, wxEXPAND | wxBOTTOM, 2);
      s3->Add(replaceL, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
      s3->Add(replace, 1, wxEXPAND | wxBOTTOM, 2);
    s->Add(s3, 0, wxEXPAND | (wxALL & ~wxBOTTOM), 8);
    s->AddSpacer(2);
    s->Add(whole_word, 0, wxALL & ~wxBOTTOM & ~wxTOP, 8);
    s->AddSpacer(4);
    s->Add(enabled,    0, wxALL & ~wxTOP & ~wxBOTTOM, 8);
    // buttons
    wxSizer* s4 = new wxBoxSizer(wxHORIZONTAL);
      s4->Add(defaults,                           0, wxALL & ~wxRIGHT, 8);
      s4->Add(CreateButtonSizer(wxOK | wxCANCEL), 1, wxALL, 8);
    s->Add(s4, 0, wxEXPAND);
  s->SetSizeHints(this);
  SetSizer(s);
  // Set default size
  SetSize(350, 450);
  // initialize values
  use_auto_replace->SetValue(list->gs.use_auto_replace);
  enable();
  refreshItem();
}

void AutoReplaceWindow::onItemSelect(wxCommandEvent&) {
  refreshItem();
}
void AutoReplaceWindow::onItemChange(wxCommandEvent&) {
  updateItem();
}

void AutoReplaceWindow::onEnable(wxCommandEvent&) {
  enable();
}

void AutoReplaceWindow::onOk(wxCommandEvent&) {
  store();
  Hide();
}

void AutoReplaceWindow::onRemove(wxCommandEvent&) {
  list->removeSelected();
}
void AutoReplaceWindow::onAdd(wxCommandEvent&) {
  list->addItem(make_intrusive<AutoReplace>());
}
void AutoReplaceWindow::onDefault(wxCommandEvent&) {
  use_auto_replace->SetValue(true);
  list->reset();
  enable();
}

void AutoReplaceWindow::enable() {
  bool enable = use_auto_replace->GetValue();
  list      ->Enable(enable);
  matchL    ->Enable(enable);
  replaceL  ->Enable(enable);
  match     ->Enable(enable);
  replace   ->Enable(enable);
  enabled   ->Enable(enable);
  whole_word->Enable(enable);
  add       ->Enable(enable);
  remove    ->Enable(enable);
}

void AutoReplaceWindow::refreshItem() {
  if (in_event) return;
  if (!use_auto_replace->GetValue()) return;
  in_event = true;
  AutoReplaceP ar = list->getSelected();
  match     ->Enable(ar && ar->custom);
  replace   ->Enable((bool)ar);
  matchL    ->Enable(ar && ar->custom);
  replaceL  ->Enable((bool)ar);
  enabled   ->Enable((bool)ar);
  whole_word->Enable((bool)ar);
  remove    ->Enable(ar && ar->custom);
  if (ar) {
    match     ->SetValue(ar->match);
    replace   ->SetValue(ar->replace);
    enabled   ->SetValue(ar->enabled);
    whole_word->SetValue(ar->whole_word);
  } else {
    match     ->SetValue(wxEmptyString);
    replace   ->SetValue(wxEmptyString);
    enabled   ->SetValue(false);
    whole_word->SetValue(false);
  }
  in_event = false;
}

void AutoReplaceWindow::updateItem() {
  if (in_event) return;
  in_event = true;
  AutoReplaceP ar = list->getSelected();
  if (!ar) return;
  ar->match      = match->GetValue();
  ar->replace    = replace->GetValue();
  ar->enabled    = enabled->GetValue();
  ar->whole_word = whole_word->GetValue();
  list->refreshList(true);
  in_event = false;
}

void AutoReplaceWindow::store() {
  list->gs.use_auto_replace = use_auto_replace->GetValue();
  swap(list->items, list->gs.auto_replaces);
}

BEGIN_EVENT_TABLE(AutoReplaceWindow, wxDialog)
  EVT_COMMAND    (wxID_ANY, EVENT_ITEM_SELECT, AutoReplaceWindow::onItemSelect)
  EVT_TEXT    (ID_ITEM_VALUE,  AutoReplaceWindow::onItemChange)
  EVT_CHECKBOX  (ID_ITEM_VALUE,  AutoReplaceWindow::onItemChange)
  EVT_CHECKBOX  (ID_USE_AUTO_REPLACE, AutoReplaceWindow::onEnable)
  EVT_BUTTON    (wxID_OK,        AutoReplaceWindow::onOk)
  EVT_BUTTON    (ID_ADD_ITEM,    AutoReplaceWindow::onAdd)
  EVT_BUTTON    (ID_REMOVE_ITEM, AutoReplaceWindow::onRemove)
  EVT_BUTTON    (ID_DEFAULTS,    AutoReplaceWindow::onDefault)
END_EVENT_TABLE()
