/*
 * Copyright (C) 2017 ~ 2018 Deepin Technology Co., Ltd.
 *
 * 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
 * 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 "ui/widgets/popup_menu.h"

#include <QApplication>
#include <QDebug>
#include <QKeyEvent>
#include <QListView>
#include <QPainter>
#include <QStringListModel>
#include <QHBoxLayout>
#include <QPainterPath>

#include "base/file_util.h"
#include "ui/delegates/popup_menu_delegate.h"

namespace installer {

namespace {

const int kBorderRadius = 8;
const int kBorderDiameter = kBorderRadius * 2;

// _____
// \ | /
//  \|/
const int kTriangleHeight = 6;

// Height of menu item is also defined in styles/popup_menu.css
const int kMenuItemHeight = 24;
const int kMenuViewVerticalMargin = 4;
const int kMenuViewBottomPadding = 5;
const int kMenuViewHorizontalMargins = 20;
const int kMenuViewMinimumWidth = 60;

}  // namespace

PopupMenu::PopupMenu(QWidget* parent) : QFrame(parent) {
  this->setObjectName("popup_menu");

  this->initUI();
  this->initConnections();
}

QStringList PopupMenu::stringList() const {
  return menu_model_->stringList();
}

void PopupMenu::popup(const QPoint& pos) {
  const QSize size = menu_view_->size();
  this->move(pos.x() - size.width() / 2, pos.y() - size.height());
  //this->resize(size.width(), size.height() + kTriangleHeight);
  this->show();

  // Grab global keyboard events when menu window is popup.
  this->grabKeyboard();
}

void PopupMenu::setStringList(const QStringList& strings) {
  menu_model_->setStringList(strings);

  int item_width = kMenuViewMinimumWidth;
  const QFontMetrics metrics(menu_view_->font());
  for (const QString& str : strings) {
    const int curr_width = metrics.width(str);
    item_width = (curr_width > item_width) ? curr_width : item_width;
  }
  // Add margin to list view.
  const int width = item_width + kMenuViewHorizontalMargins * 2;

  // Add more space at bottom of list view.
  const int height = kMenuItemHeight * strings.length() +
                     kMenuViewVerticalMargin * 2 + kMenuViewBottomPadding;

  this->resize(width, height + kTriangleHeight);
  menu_view_->setFixedSize(width, height);
  menu_view_->adjustSize();
  adjustSize();
  update();
}

void PopupMenu::hideEvent(QHideEvent* event) {
  // No need to monitor global mouse event when menu is hidden.
  qApp->removeEventFilter(this);

  this->releaseKeyboard();
  QWidget::hideEvent(event);
  emit this->onHide();
}

void PopupMenu::keyPressEvent(QKeyEvent* event) {
  if (event->key() == Qt::Key_Escape) {
    this->hide();
  }
  QWidget::keyPressEvent(event);
}

void PopupMenu::paintEvent(QPaintEvent* event) {
  Q_UNUSED(event);

  QPainter painter(this);
  painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing,
                         true);
  const int kWidth = this->width();
  const int kHalfWidth = kWidth / 2;
  const int kHeight = this->height();

  // First draw background with round corner.
  QPainterPath background_path;
  background_path.moveTo(kWidth, kBorderRadius);
  background_path.arcTo(kWidth - kBorderDiameter, 0,
                        kBorderDiameter, kBorderDiameter,
                        0.0, 90.0);
  background_path.lineTo(kBorderRadius, 0);
  background_path.arcTo(0, 0, kBorderDiameter, kBorderDiameter, 90.0, 90.0);
  background_path.lineTo(0, kHeight - kBorderRadius - kTriangleHeight);
  background_path.arcTo(0, kHeight - kBorderDiameter - kTriangleHeight,
                        kBorderDiameter, kBorderDiameter,
                        180.0, 90.0);
  background_path.lineTo(kHalfWidth - kTriangleHeight,
                         kHeight - kTriangleHeight);
  // Draw isosceles right-angled triangle in bottom center of label.
  background_path.lineTo(kHalfWidth, kHeight);
  background_path.lineTo(kHalfWidth + kTriangleHeight,
                         kHeight - kTriangleHeight);
  background_path.lineTo(kWidth - kBorderRadius, kHeight - kTriangleHeight);
  background_path.arcTo(kWidth - kBorderDiameter,
                        kHeight - kBorderDiameter - kTriangleHeight,
                        kBorderDiameter, kBorderDiameter,
                        270.0, 90.0);
  background_path.closeSubpath();
  const QColor background_color(255, 255, 255, 230);
  painter.fillPath(background_path, QBrush(background_color));
}

void PopupMenu::showEvent(QShowEvent* event) {
  qApp->installEventFilter(this);
  QFrame::showEvent(event);
}

void PopupMenu::initConnections() {
  connect(menu_view_, &QListView::pressed,
          this, &PopupMenu::onMenuViewActivated);
}

void PopupMenu::initUI() {
  menu_model_ = new QStringListModel(this);
  menu_view_ = new QListView(this);
  menu_view_->setObjectName("menu_view");
  menu_view_->setContentsMargins(0, kMenuViewVerticalMargin,
                                 0, kMenuViewVerticalMargin);
  menu_view_->setModel(menu_model_);
  menu_view_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  menu_view_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  menu_view_->setUniformItemSizes(true);
  menu_view_->setSelectionMode(QListView::SingleSelection);
  menu_view_->setEditTriggers(QListView::NoEditTriggers);
  PopupMenuDelegate* popup_delegate = new PopupMenuDelegate(this);
  menu_view_->setItemDelegate(popup_delegate);
  menu_view_->setMouseTracking(true);
  menu_view_->setStyleSheet(ReadFile(":/styles/popup_menu.css"));

  this->setContentsMargins(0, 0, 0, 0);

  QHBoxLayout* mainLayout = new QHBoxLayout;
  mainLayout->setMargin(0);
  mainLayout->setSpacing(0);
  mainLayout->addWidget(menu_view_);

  setLayout(mainLayout);
}

void PopupMenu::onMenuViewActivated(const QModelIndex& index) {
  Q_ASSERT(index.isValid());
  if (index.isValid()) {
    emit this->menuActivated(index.row());
  }
}

}  // namespace installer
