#include "items_pane.h"

#include <map>

namespace {

enum SortInstruction {
  SI_NONE = 0,
  SI_ASC = 1 << 0,
  SI_DESC = 1 << 1,
  SI_NAME = 1 << 2,
  SI_AMOUNT = 1 << 3,
  SI_ORDER = 1 << 4,
};

inline SortInstruction operator|(SortInstruction lhs, SortInstruction rhs) {
  return static_cast<SortInstruction>(static_cast<int>(lhs) |
                                      static_cast<int>(rhs));
}

template <typename T>
int ItemCompare(const T& lhs, const T& rhs, bool ascending) {
  if (lhs < rhs) {
    return ascending ? -1 : 1;
  } else if (lhs > rhs) {
    return ascending ? 1 : -1;
  } else {
    return 0;
  }
}

int wxCALLBACK RowCompare(wxIntPtr item1, wxIntPtr item2, wxIntPtr sortData) {
  const ItemState& lhs = *reinterpret_cast<const ItemState*>(item1);
  const ItemState& rhs = *reinterpret_cast<const ItemState*>(item2);
  SortInstruction instruction = static_cast<SortInstruction>(sortData);

  bool ascending = (instruction & SI_ASC) != 0;
  if ((instruction & SI_NAME) != 0) {
    return ItemCompare(lhs.name, rhs.name, ascending);
  } else if ((instruction & SI_AMOUNT) != 0) {
    return ItemCompare(lhs.amount, rhs.amount, ascending);
  } else if ((instruction & SI_ORDER) != 0) {
    return ItemCompare(lhs.index, rhs.index, ascending);
  } else {
    return 0;
  }
}

}  // namespace

ItemsPane::ItemsPane(wxWindow* parent)
    : wxListView(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                 wxLC_REPORT | wxLC_SINGLE_SEL | wxLC_HRULES) {
  AppendColumn("Item", wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE_USEHEADER);
  AppendColumn("Amount", wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE_USEHEADER);
  AppendColumn("Order", wxLIST_FORMAT_LEFT, wxLIST_AUTOSIZE_USEHEADER);

  Bind(wxEVT_LIST_COL_CLICK, &ItemsPane::OnColClick, this);
  Bind(wxEVT_DPI_CHANGED, &ItemsPane::OnDPIChanged, this);
}

void ItemsPane::ResetIndicators() {
  DeleteAllItems();
  items_.clear();
}

void ItemsPane::UpdateIndicators(const std::vector<ItemState>& items) {
  std::map<std::string, ItemState> items_by_name;

  for (const ItemState& item : items) {
    items_by_name[item.name] = item;
  }

  for (int i = 0; i < GetItemCount(); i++) {
    std::string item_name = GetItemText(i).utf8_string();
    auto it = items_by_name.find(item_name);

    if (it != items_by_name.end()) {
      SetItem(i, 1, std::to_string(it->second.amount));
      SetItem(i, 2, std::to_string(it->second.index));

      *reinterpret_cast<ItemState*>(GetItemData(i)) = it->second;

      items_by_name.erase(item_name);
    }
  }

  for (const auto& [name, item] : items_by_name) {
    int i = InsertItem(GetItemCount(), name);
    SetItem(i, 1, std::to_string(item.amount));
    SetItem(i, 2, std::to_string(item.index));

    auto item_ptr = std::make_unique<ItemState>(item);
    SetItemPtrData(i, reinterpret_cast<wxUIntPtr>(item_ptr.get()));
    items_.push_back(std::move(item_ptr));
  }

  SetColumnWidth(0, wxLIST_AUTOSIZE);
  SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER);
  SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER);

  if (GetSortIndicator() != -1) {
    DoSort(GetSortIndicator(), IsAscendingSortIndicator());
  }
}

void ItemsPane::OnColClick(wxListEvent& event) {
  int col = event.GetColumn();
  if (col == -1) {
    return;
  }

  bool ascending = GetUpdatedAscendingSortIndicator(col);

  DoSort(col, ascending);
}

void ItemsPane::OnDPIChanged(wxDPIChangedEvent& event) {
  SetColumnWidth(0, wxLIST_AUTOSIZE);
  SetColumnWidth(1, wxLIST_AUTOSIZE_USEHEADER);
  SetColumnWidth(2, wxLIST_AUTOSIZE_USEHEADER);

  event.Skip();
}

void ItemsPane::DoSort(int col, bool ascending) {
  SortInstruction instruction = SI_NONE;
  if (ascending) {
    instruction = instruction | SI_ASC;
  } else {
    instruction = instruction | SI_DESC;
  }

  if (col == 0) {
    instruction = instruction | SI_NAME;
  } else if (col == 1) {
    instruction = instruction | SI_AMOUNT;
  } else if (col == 2) {
    instruction = instruction | SI_ORDER;
  }

  if (SortItems(RowCompare, instruction)) {
    ShowSortIndicator(col, ascending);
  }
}