/* This file is part of Clementine.
   Copyright 2010, David Sansome <me@davidsansome.com>

   Clementine 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.

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

#include "globalsearch.h"
#include "globalsearchtooltip.h"
#include "tooltipactionwidget.h"
#include "tooltipresultwidget.h"
#include "core/logging.h"
#include "core/utilities.h"

#include <QAction>
#include <QApplication>
#include <QDesktopWidget>
#include <QKeyEvent>
#include <QLayoutItem>
#include <QPainter>
#include <QSettings>
#include <QVBoxLayout>

const qreal GlobalSearchTooltip::kBorderRadius = 8.0;
const qreal GlobalSearchTooltip::kTotalBorderWidth = 4.0;
const qreal GlobalSearchTooltip::kOuterBorderWidth = 0.5;
const qreal GlobalSearchTooltip::kArrowWidth = 10.0;
const qreal GlobalSearchTooltip::kArrowHeight = 10.0;


GlobalSearchTooltip::GlobalSearchTooltip(QWidget* event_target)
  : QWidget(NULL),
    desktop_(qApp->desktop()),
    event_target_(event_target),
    active_result_(0),
    show_tooltip_help_(true)
{
  setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
  setFocusPolicy(Qt::NoFocus);
  setAttribute(Qt::WA_NoSystemBackground);
  setAttribute(Qt::WA_TranslucentBackground);

  switch_action_ = new QAction(tr("Switch provider"), this);
  switch_action_->setShortcut(QKeySequence(Qt::Key_Tab));
  connect(switch_action_, SIGNAL(triggered()), SLOT(SwitchProvider()));

  ReloadSettings();
}

void GlobalSearchTooltip::SetResults(const SearchProvider::ResultList& results) {
  results_ = results;

  qDeleteAll(widgets_);
  widgets_.clear();
  result_buttons_.clear();

  active_result_ = 0;

  // Using a QVBoxLayout here made some weird flickering that I couldn't figure
  // out how to fix, so do layout manually.
  int w = 0;
  int y = 9;

  // Add a widget for each result
  foreach (const SearchProvider::Result& result, results) {
    TooltipResultWidget* widget = new TooltipResultWidget(result, this);
    if (widgets_.isEmpty()) {
      // If this is the first widget then mark it as selected
      widget->setChecked(true);
    }

    AddWidget(widget, &w, &y);
    result_buttons_ << widget;
  }

  if (show_tooltip_help_) {
    // Add the action widget
    QList<QAction*> actions;
    if (results_.count() > 1) {
      actions.append(switch_action_);
    }
    actions.append(common_actions_);

    action_widget_ = new TooltipActionWidget(this);
    action_widget_->SetActions(actions);
    AddWidget(action_widget_, &w, &y);
  }

  // Set the width of each widget
  foreach (QWidget* widget, widgets_) {
    widget->resize(w, widget->sizeHint().height());
  }

  // Resize this widget
  y += 9;
  resize(w, y);

  inner_rect_ = rect().adjusted(
        kArrowWidth + kTotalBorderWidth, kTotalBorderWidth,
        -kTotalBorderWidth, -kTotalBorderWidth);

  foreach (QWidget* widget, widgets_) {
    widget->setMask(inner_rect_);
  }
}

void GlobalSearchTooltip::AddWidget(QWidget* widget, int* w, int* y) {
  widget->move(0, *y);
  widget->show();
  widgets_ << widget;

  QSize size_hint(widget->sizeHint());
  *y += size_hint.height();
  *w = qMax(*w, size_hint.width());
}

void GlobalSearchTooltip::ShowAt(const QPoint& pointing_to) {
  const qreal min_arrow_offset = kBorderRadius + kArrowHeight;
  const QRect screen = desktop_->screenGeometry(this);

  arrow_offset_ = min_arrow_offset +
                  qMax(0, pointing_to.y() + height() - screen.bottom());

  move(pointing_to.x(), pointing_to.y() - arrow_offset_);

  if (!isVisible())
    show();
}

bool GlobalSearchTooltip::event(QEvent* e) {
  switch (e->type()) {
  case QEvent::KeyPress: {
    QKeyEvent* ke = static_cast<QKeyEvent*>(e);
    if (ke->key() == Qt::Key_Tab && ke->modifiers() == Qt::NoModifier) {
      SwitchProvider();
      e->accept();
      return true;
    }

    // fallthrough
  }
  case QEvent::KeyRelease:
  case QEvent::InputMethod:
  case QEvent::Shortcut:
  case QEvent::ShortcutOverride:
    if (QApplication::sendEvent(event_target_, e)) {
      return true;
    }
    break;

  case QEvent::MouseButtonPress:
  case QEvent::MouseButtonRelease:
  case QEvent::MouseButtonDblClick:
    if (!underMouse()) {
      const QMouseEvent* me = static_cast<QMouseEvent*>(e);
      QWidget* child = event_target_->childAt(
            event_target_->mapFromGlobal(me->globalPos()));

      if (child)
        child->setAttribute(Qt::WA_UnderMouse, true);

      Utilities::ForwardMouseEvent(me, child ? child : event_target_);
      return true;
    }
    break;

  default:
    break;
  }

  return QWidget::event(e);
}

void GlobalSearchTooltip::paintEvent(QPaintEvent*) {
  QPainter p(this);

  const QColor outer_color(0, 0, 0, 192);
  const QColor inner_color = palette().color(QPalette::Highlight);
  const QColor center_color = palette().color(QPalette::Base);

  // Transparent background
  p.fillRect(rect(), Qt::transparent);

  QRect area(inner_rect_.adjusted(
      -kTotalBorderWidth/2, -kTotalBorderWidth/2,
      kTotalBorderWidth/2, kTotalBorderWidth/2));

  // Draw the border
  p.setCompositionMode(QPainter::CompositionMode_Source);
  p.setRenderHint(QPainter::Antialiasing);
  p.setPen(QPen(outer_color, kTotalBorderWidth));
  p.drawRoundedRect(area, kBorderRadius, kBorderRadius);

  // Draw the arrow
  QPolygonF arrow;
  arrow << QPointF(kArrowWidth + 2, arrow_offset_ - kArrowHeight)
        << QPointF(0, arrow_offset_)
        << QPointF(kArrowWidth + 2, arrow_offset_ + kArrowHeight);

  p.setBrush(outer_color);
  p.setPen(outer_color);
  p.drawPolygon(arrow);

  // Now draw the inner shapes on top
  const qreal inner_border_width = kTotalBorderWidth - kOuterBorderWidth;

  QRect inner_area(inner_rect_.adjusted(
      -inner_border_width/2, -inner_border_width/2,
      inner_border_width/2, inner_border_width/2));

  // Inner border
  p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  p.setBrush(center_color);
  p.setPen(QPen(inner_color, inner_border_width));
  p.drawRoundedRect(inner_area, kBorderRadius, kBorderRadius);

  // Inner arrow
  arrow[0].setY(arrow[0].y() + kOuterBorderWidth + 0.5);
  arrow[1].setX(arrow[1].x() + kOuterBorderWidth + 1.5);
  arrow[2].setY(arrow[2].y() - kOuterBorderWidth - 0.5);

  p.setBrush(inner_color);
  p.setPen(inner_color);
  p.drawPolygon(arrow);
}

void GlobalSearchTooltip::SwitchProvider() {
  // Find which one was checked before.
  int old_index = -1;
  for (int i=0 ; i<result_buttons_.count() ; ++i) {
    if (result_buttons_[i]->isChecked()) {
      old_index = i;
      break;
    }
  }

  if (old_index == -1)
    return;

  // Check the new one.  The auto exclusive group will take care of unchecking
  // the old one.
  active_result_ = (old_index + 1) % result_buttons_.count();
  result_buttons_[active_result_]->setChecked(true);
}

void GlobalSearchTooltip::ReloadSettings() {
  QSettings s;
  s.beginGroup(GlobalSearch::kSettingsGroup);

  show_tooltip_help_ = s.value("tooltip_help", true).toBool();
}
