/**********************************************************************************************
    Copyright (C) 2014 Oliver Eichler <oliver.eichler@gmx.de>

    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, either version 3 of the License, or
    (at your option) any later version.

    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, see <http://www.gnu.org/licenses/>.

**********************************************************************************************/

#include "map/CMapList.h"

#include <QtWidgets>

#include "CMainWindow.h"
#include "map/CMapDraw.h"
#include "map/CMapItem.h"
#include "map/CMapItemDelegate.h"
#include "misc.h"
#include "svgticon/CSvgtIcon.h"

CMapTreeWidget::CMapTreeWidget(QWidget* parent) : QTreeWidget(parent) {
  CMapItemDelegate* delegate = new CMapItemDelegate(this);
  setItemDelegate(delegate);
  QObject::connect(delegate, &CMapItemDelegate::sigUpdateItem, this, &CMapTreeWidget::slotUpdateItem);
}

void CMapTreeWidget::slotUpdateItem(const QString& key) {
  const int N = topLevelItemCount();
  for (int n = 0; n < N; n++) {
    CMapItem* map = dynamic_cast<CMapItem*>(topLevelItem(n));
    if (map == nullptr || map->getKey() != key) {
      continue;
    }
    viewport()->update(visualItemRect(map));
    break;
  }
}

void CMapTreeWidget::keyPressEvent(QKeyEvent* e) {
  const QKeyCombination keyCombination = e->keyCombination();
  if (keyCombination == QKeyCombination(Qt::ShiftModifier, Qt::Key_Home) ||
      keyCombination == QKeyCombination(Qt::ShiftModifier | Qt::KeypadModifier, Qt::Key_Home)) {
    emit sigMoveHome();
  } else if (keyCombination == QKeyCombination(Qt::ShiftModifier, Qt::Key_Up) ||
             keyCombination == QKeyCombination(Qt::ShiftModifier | Qt::KeypadModifier, Qt::Key_Up)) {
    emit sigMoveUp();
  } else if (keyCombination == QKeyCombination(Qt::ShiftModifier, Qt::Key_Down) ||
             keyCombination == QKeyCombination(Qt::ShiftModifier | Qt::KeypadModifier, Qt::Key_Down)) {
    emit sigMoveDown();
  } else if (keyCombination == QKeyCombination(Qt::ShiftModifier, Qt::Key_End) ||
             keyCombination == QKeyCombination(Qt::ShiftModifier | Qt::KeypadModifier, Qt::Key_End)) {
    emit sigMoveEnd();
  } else {
    QTreeWidget::keyPressEvent(e);
  }
}

void CMapTreeWidget::dragEnterEvent(QDragEnterEvent* e) {
  collapseAll();
  QTreeWidget::dragEnterEvent(e);
}

void CMapTreeWidget::dragLeaveEvent(QDragLeaveEvent* e) {
  CMapItem* item = dynamic_cast<CMapItem*>(currentItem());
  if (item) {
    item->showChildren(false);
  }

  QTreeWidget::dragLeaveEvent(e);

  if (item) {
    item->showChildren(true);
  }
}

void CMapTreeWidget::dropEvent(QDropEvent* e) {
  CMapItem* item = dynamic_cast<CMapItem*>(currentItem());
  if (item) {
    item->showChildren(false);
  }

  QTreeWidget::dropEvent(e);

  if (item) {
    item->showChildren(true);
  }

  setCurrentItem(nullptr);
  emit sigChanged();
}

CMapList::CMapList(CCanvas* parent) : QWidget(parent), canvas(parent) {
  setupUi(this);
  CSvgtIcon::load(labelIcon, "://icons/Help.svgt");
  lineFilter->addAction(actionClearFilter, QLineEdit::TrailingPosition);

  connect(treeWidget, &CMapTreeWidget::customContextMenuRequested, this, &CMapList::slotContextMenu);
  connect(treeWidget, &CMapTreeWidget::sigChanged, this, &CMapList::sigChanged);
  connect(treeWidget, &CMapTreeWidget::sigMoveHome, this, &CMapList::slotMoveHome);
  connect(treeWidget, &CMapTreeWidget::sigMoveUp, this, &CMapList::slotMoveUp);
  connect(treeWidget, &CMapTreeWidget::sigMoveDown, this, &CMapList::slotMoveDown);
  connect(treeWidget, &CMapTreeWidget::sigMoveEnd, this, &CMapList::slotMoveEnd);
  connect(actionMoveHome, &QAction::triggered, this, &CMapList::slotMoveHome);
  connect(actionMoveUp, &QAction::triggered, this, &CMapList::slotMoveUp);
  connect(actionMoveDown, &QAction::triggered, this, &CMapList::slotMoveDown);
  connect(actionMoveEnd, &QAction::triggered, this, &CMapList::slotMoveEnd);
  connect(actionRemove, &QAction::triggered, this, &CMapList::slotRemove);
  connect(actionReloadMaps, &QAction::triggered, this, &CMapList::slotReloadMaps);
  connect(actionOverviewInfo, &QAction::triggered, this, &CMapList::slotOverviewInfo);
  connect(labelHelpFillMapList, &QLabel::linkActivated, &CMainWindow::self(),
          static_cast<void (CMainWindow::*)(const QString&)>(&CMainWindow::slotLinkActivated));
  connect(lineFilter, &QLineEdit::textChanged, this, &CMapList::slotFilter);

  menu = new QMenu(this);
  menu->addAction(actionMoveHome);
  menu->addAction(actionMoveUp);
  menu->addAction(actionMoveDown);
  menu->addAction(actionMoveEnd);
  menu->addSeparator();
  menu->addAction(actionRemove);
  menu->addSeparator();
  menu->addAction(actionOverviewInfo);
  menu->addSeparator();
  menu->addAction(actionReloadMaps);
  menu->addAction(CMainWindow::self().getMapSetupAction());
  menu->setToolTipsVisible(true);
}

CMapList::~CMapList() {}

QString CMapList::getCanvasKey() const {
  if (canvas) {
    return canvas->getKey();
  }
  return "";
}

void CMapList::addToTabWidget(QTabWidget* widget) {
  if (canvas == nullptr) {
    return;
  }
  tabWidget = widget;
  tabWidget->addTab(this, canvas->getName());

  // Those two connection only live as long as tabWidget or canvas.
  // Therefore we do not need to check for valid pointers inside the lambdas
  connect(canvas, &CCanvas::sigCanvasIsCurrent, tabWidget, [this]() { tabWidget->setCurrentWidget(this); });
  connect(canvas, &CCanvas::sigNameChanged, tabWidget, [this](const CCanvas& c) {
    const int idx = tabWidget->indexOf(this);
    tabWidget->setTabText(idx, c.getName());
  });
}

void CMapList::addMap(CMapItem* map) {
  treeWidget->addTopLevelItem(map);
  connect(map, &CMapItem::sigChanged, this, &CMapList::sigChanged);
}

void CMapList::clear() { treeWidget->clear(); }

int CMapList::count() { return treeWidget->topLevelItemCount(); }

CMapItem* CMapList::item(int i) { return dynamic_cast<CMapItem*>(treeWidget->topLevelItem(i)); }

void CMapList::updateHelpText() {
  bool haveMaps = (treeWidget->topLevelItemCount() > 0);

  labelHelpFillMapList->setVisible(!haveMaps);

  if (!haveMaps) {
    labelIcon->show();
    labelHelpActivateMap->hide();
  } else {
    bool showHelp = true;

    const int N = treeWidget->topLevelItemCount();
    for (int n = 0; n < N; n++) {
      CMapItem* map = dynamic_cast<CMapItem*>(treeWidget->topLevelItem(n));
      if (map == nullptr) {
        continue;
      }
      if (map->isActivated()) {
        showHelp = false;
      }
    }
    labelIcon->setVisible(showHelp);
    labelHelpActivateMap->setVisible(showHelp);
  }
}

void CMapList::moveMapToTop(CMapItem* map) {
  const int index = treeWidget->indexOfTopLevelItem(map);
  if (index == NOIDX) {
    return;
  }
  map->showChildren(false);
  treeWidget->takeTopLevelItem(index);
  treeWidget->insertTopLevelItem(0, map);
  map->showChildren(true);
}

void CMapList::moveMapItem(CMapItem* map, const int from, const int to) {
  map->showChildren(false);
  treeWidget->takeTopLevelItem(from);
  treeWidget->insertTopLevelItem(to, map);
  map->showChildren(true);
  treeWidget->setCurrentItem(map);
  emit treeWidget->sigChanged();
}

void CMapList::slotMoveHome() {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());
  if (item != nullptr) {
    int from = treeWidget->currentIndex().row();
    if (from > 0) {
      moveMapItem(item, from, 0);
    }
  }
}

void CMapList::slotMoveUp() {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());
  if (item != nullptr) {
    int from = treeWidget->currentIndex().row();
    if (from > 0) {
      moveMapItem(item, from, from - 1);
    }
  }
}

void CMapList::slotMoveDown() {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());
  if (item != nullptr) {
    int from = treeWidget->currentIndex().row();
    int last = treeWidget->topLevelItemCount() - 1;
    if (from < last) {
      moveMapItem(item, from, from + 1);
    }
  }
}

void CMapList::slotMoveEnd() {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());
  if (item != nullptr) {
    int from = treeWidget->currentIndex().row();
    int last = treeWidget->topLevelItemCount() - 1;
    if (from < last) {
      moveMapItem(item, from, last);
    }
  }
}

void CMapList::slotRemove() {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());
  if (item == nullptr) {
    return;
  }

  int index = treeWidget->indexOfTopLevelItem(item);
  if (index == NOIDX) {
    return;
  }

  delete treeWidget->takeTopLevelItem(index);
}

void CMapList::slotContextMenu(const QPoint& point) {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());

  bool itemIsSelected = nullptr != item;

  actionMoveHome->setEnabled(itemIsSelected && (treeWidget->itemAbove(item) != 0));
  actionMoveUp->setEnabled(itemIsSelected && (treeWidget->itemAbove(item) != 0));
  actionMoveDown->setEnabled(itemIsSelected && (treeWidget->itemBelow(item) != 0));
  actionMoveEnd->setEnabled(itemIsSelected && (treeWidget->itemBelow(item) != 0));
  actionRemove->setVisible(itemIsSelected && item->getStatus() == IMapItem::eStatus::Missing);
  actionOverviewInfo->setVisible(itemIsSelected && item->hasOverviewInfo());

  QPoint p = treeWidget->mapToGlobal(point);
  menu->exec(p);
}

void CMapList::slotOverviewInfo() {
  CMapItem* item = dynamic_cast<CMapItem*>(treeWidget->currentItem());
  if (item != nullptr) {
    item->triggerOverviewAdvisory();
  }
}

static void saveResource(const QString& name, QDir& dir) {
  QFile resource1(QString("://map/%1").arg(name));
  openFileCheckSuccess(QIODevice::ReadOnly, resource1);

  QFile file(dir.absoluteFilePath(name));
  openFileCheckSuccess(QIODevice::WriteOnly, file);
  file.write(resource1.readAll());
  file.close();
}

void CMapList::slotMapHonk() {
  QString mapPath = CMainWindow::self().getMapsPath();
  if (mapPath.isEmpty()) {
    mapPath = QDir::homePath();
  }

  mapPath = QFileDialog::getExistingDirectory(CMainWindow::getBestWidgetForParent(),
                                              tr("Where do you want to store maps?"), mapPath);
  if (mapPath.isEmpty()) {
    return;
  }

  QDir dir(mapPath);

  saveResource("WorldSat.wmts", dir);
  saveResource("WorldTopo.wmts", dir);
  saveResource("OpenStreetMap.tms", dir);
  saveResource("OSM_Topo.tms", dir);
  saveResource("OpenCycleMap.tms", dir);

  CMapDraw::setupMapPath(mapPath);

  CCanvas* canvas = CMainWindow::self().getVisibleCanvas();
  if (canvas) {
    canvas->setScales(CCanvas::eScalesSquare);
  }
}

void CMapList::slotReloadMaps() {
  CMapItemDelegate* delegate = dynamic_cast<CMapItemDelegate*>(treeWidget->itemDelegate());
  if (delegate != nullptr) {
    delegate->reset();
  }
  CMapDraw::setupMapPath(CMapDraw::getMapPaths());
}

void CMapList::slotFilter(const QString& str) {
  actionClearFilter->setIcon(str.isEmpty() ? QIcon("://icons/Filter.svgt") : QIcon("://icons/Cancel.svgt"));

  const int N = treeWidget->topLevelItemCount();

  if (str.isEmpty()) {
    for (int n = 0; n < N; n++) {
      CMapItem* map = dynamic_cast<CMapItem*>(treeWidget->topLevelItem(n));
      if (map == nullptr) {
        continue;
      }
      map->setHidden(false);
    }
  } else {
    const QString& tmp = str.toUpper();
    for (int n = 0; n < N; n++) {
      CMapItem* map = dynamic_cast<CMapItem*>(treeWidget->topLevelItem(n));
      if (map == nullptr) {
        continue;
      }

      map->setHidden(!map->getName().toUpper().contains(tmp));
    }
  }
}
