#include "layerfooterpanel.h"

#include <QPainter>
#include <QToolTip>
#include <QScrollBar>

#include "xsheetviewer.h"
#include "xshcolumnviewer.h"

#include "tapp.h"
#include "toonz/tscenehandle.h"
#include "toonz/txsheethandle.h"
#include "toonz/tobjecthandle.h"

#include "toonz/preferences.h"

#include "toonzqt/gutil.h"

using XsheetGUI::ColumnArea;

LayerFooterPanel::LayerFooterPanel(XsheetViewer *viewer, QWidget *parent,
                                   Qt::WindowFlags flags)
    : QWidget(parent, flags), m_viewer(viewer) {
  const Orientation *o = viewer->orientation();

  setObjectName("layerFooterPanel");

  setMouseTracking(true);

  Qt::Orientation ori =
      (o->isVerticalTimeline()) ? Qt::Vertical : Qt::Horizontal;
  bool invDirection  = o->isVerticalTimeline();
  QString tooltipStr = (o->isVerticalTimeline())
                           ? tr("Zoom in/out of xsheet")
                           : tr("Zoom in/out of timeline");

  m_frameZoomSlider = new QSlider(ori, this);
  m_frameZoomSlider->setMinimum(20);
  m_frameZoomSlider->setMaximum(100);
  m_frameZoomSlider->setValue(m_viewer->getFrameZoomFactor());
  m_frameZoomSlider->setToolTip(tooltipStr);
  m_frameZoomSlider->setInvertedAppearance(invDirection);
  m_frameZoomSlider->setInvertedControls(invDirection);

  // for minimal layout, hide the slider
  if (o->rect(PredefinedRect::ZOOM_SLIDER).isEmpty()) m_frameZoomSlider->hide();

  connect(m_frameZoomSlider, SIGNAL(valueChanged(int)), this,
          SLOT(onFrameZoomSliderValueChanged(int)));
}

LayerFooterPanel::~LayerFooterPanel() {}

namespace {

QColor mix(const QColor &a, const QColor &b, double w) {
  return QColor(a.red() * w + b.red() * (1 - w),
                a.green() * w + b.green() * (1 - w),
                a.blue() * w + b.blue() * (1 - w));
}

QColor withAlpha(const QColor &color, double alpha) {
  QColor result(color);
  result.setAlpha(alpha * 255);
  return result;
}

QRect shorter(const QRect original) { return original.adjusted(0, 2, 0, -2); }

QLine leftSide(const QRect &r) { return QLine(r.topLeft(), r.bottomLeft()); }

QLine rightSide(const QRect &r) { return QLine(r.topRight(), r.bottomRight()); }
}  // namespace

void LayerFooterPanel::paintEvent(QPaintEvent *event) {
  QPainter p(this);
  p.setRenderHint(QPainter::SmoothPixmapTransform, true);

  const Orientation *o = m_viewer->orientation();

  QRect zoomSliderRect = o->rect(PredefinedRect::ZOOM_SLIDER_AREA);
  p.fillRect(zoomSliderRect, Qt::NoBrush);

  QRect sliderObjRect = o->rect(PredefinedRect::ZOOM_SLIDER);
  m_frameZoomSlider->setGeometry(sliderObjRect);

  static QPixmap zoomIn =
      recolorPixmap(svgToPixmap(getIconThemePath("actions/15/zoom_in.svg")));
  static QPixmap zoomInRollover = recolorPixmap(
      svgToPixmap(getIconThemePath("actions/15/zoom_in_rollover.svg")));
  const QRect zoomInImgRect = o->rect(PredefinedRect::ZOOM_IN);

  static QPixmap zoomOut =
      recolorPixmap(svgToPixmap(getIconThemePath("actions/15/zoom_out.svg")));
  static QPixmap zoomOutRollover = recolorPixmap(
      svgToPixmap(getIconThemePath("actions/15/zoom_out_rollover.svg")));
  const QRect zoomOutImgRect = o->rect(PredefinedRect::ZOOM_OUT);

  p.setRenderHint(QPainter::SmoothPixmapTransform, true);
  if (m_zoomInHighlighted)
    p.drawPixmap(zoomInImgRect, zoomInRollover);
  else
    p.drawPixmap(zoomInImgRect, zoomIn);

  if (m_zoomOutHighlighted)
    p.drawPixmap(zoomOutImgRect, zoomOutRollover);
  else
    p.drawPixmap(zoomOutImgRect, zoomOut);

  if (!o->isVerticalTimeline()) {
    p.setPen(m_viewer->getVerticalLineColor());
    QLine line = {leftSide(shorter(zoomOutImgRect)).translated(-2, 0)};
    p.drawLine(line);
  }
}

void LayerFooterPanel::showOrHide(const Orientation *o) {
  QRect rect = o->rect(PredefinedRect::LAYER_FOOTER_PANEL);
  setFixedSize(rect.size());
  Qt::Orientation ori =
      (o->isVerticalTimeline()) ? Qt::Vertical : Qt::Horizontal;
  bool invDirection  = o->isVerticalTimeline();
  QString tooltipStr = (o->isVerticalTimeline())
                           ? tr("Zoom in/out of xsheet")
                           : tr("Zoom in/out of timeline");

  m_frameZoomSlider->setOrientation(ori);
  m_frameZoomSlider->setToolTip(tooltipStr);
  m_frameZoomSlider->setInvertedAppearance(invDirection);
  m_frameZoomSlider->setInvertedControls(invDirection);

  // for minimal layout, hide the slider
  if (o->rect(PredefinedRect::ZOOM_SLIDER).isEmpty())
    m_frameZoomSlider->hide();
  else
    m_frameZoomSlider->show();

  show();
}

//-----------------------------------------------------------------------------
void LayerFooterPanel::enterEvent(QEvent *) {
  m_zoomInHighlighted  = false;
  m_zoomOutHighlighted = false;

  update();
}

void LayerFooterPanel::leaveEvent(QEvent *) {
  m_zoomInHighlighted  = false;
  m_zoomOutHighlighted = false;

  update();
}

void LayerFooterPanel::mousePressEvent(QMouseEvent *event) {
  const Orientation *o = m_viewer->orientation();

  if (event->button() == Qt::LeftButton) {
    // get mouse position
    QPoint pos = event->pos();
    if (o->rect(PredefinedRect::ZOOM_IN_AREA).contains(pos)) {
      int newFactor = isCtrlPressed ? m_frameZoomSlider->maximum()
                                    : m_frameZoomSlider->value() + 10;
      m_frameZoomSlider->setValue(newFactor);
    } else if (o->rect(PredefinedRect::ZOOM_OUT_AREA).contains(pos)) {
      int newFactor = isCtrlPressed ? m_frameZoomSlider->minimum()
                                    : m_frameZoomSlider->value() - 10;
      m_frameZoomSlider->setValue(newFactor);
    }
  }

  update();
}

void LayerFooterPanel::mouseMoveEvent(QMouseEvent *event) {
  const Orientation *o = m_viewer->orientation();

  QPoint pos = event->pos();

  m_zoomInHighlighted  = false;
  m_zoomOutHighlighted = false;
  if (o->rect(PredefinedRect::ZOOM_IN_AREA).contains(pos)) {
    m_zoomInHighlighted = true;
    m_tooltip           = tr("Zoom in (Ctrl-click to zoom in all the way)");
  } else if (o->rect(PredefinedRect::ZOOM_OUT_AREA).contains(pos)) {
    m_zoomOutHighlighted = true;
    m_tooltip            = tr("Zoom out (Ctrl-click to zoom out all the way)");
  } else {
    m_tooltip = tr("");
  }

  m_pos = pos;

  update();
}

//-----------------------------------------------------------------------------

bool LayerFooterPanel::event(QEvent *event) {
  if (event->type() == QEvent::ToolTip) {
    if (!m_tooltip.isEmpty())
      QToolTip::showText(mapToGlobal(m_pos), m_tooltip);
    else
      QToolTip::hideText();
  }
  return QWidget::event(event);
}

//-----------------------------------------------------------------------------

void LayerFooterPanel::contextMenuEvent(QContextMenuEvent *event) {
  if (!m_viewer->orientation()->isVerticalTimeline()) {
    event->ignore();
    return;
  }

  QList<int> frames = m_viewer->availableFramesPerPage();
  if (frames.isEmpty()) {
    return;
  }

  QMenu *menu = new QMenu(this);
  for (auto frame : frames) {
    QAction *action = menu->addAction(tr("%1 frames per page").arg(frame));
    action->setData(frame);
    connect(action, SIGNAL(triggered()), this, SLOT(onFramesPerPageSelected()));
  }
  menu->exec(event->globalPos());
}

//-----------------------------------------------------------------------------

void LayerFooterPanel::setZoomSliderValue(int val) {
  if (val > m_frameZoomSlider->maximum())
    val = m_frameZoomSlider->maximum();
  else if (val < m_frameZoomSlider->minimum())
    val = m_frameZoomSlider->minimum();

  m_frameZoomSlider->blockSignals(true);
  m_frameZoomSlider->setValue(val);
  m_frameZoomSlider->blockSignals(false);
}

//-----------------------------------------------------------------------------

void LayerFooterPanel::onFrameZoomSliderValueChanged(int val) {
  m_viewer->zoomOnFrame(m_viewer->getCurrentRow(), val);
}

//-----------------------------------------------------------------------------

void LayerFooterPanel::onFramesPerPageSelected() {
  QAction *action = (QAction *)QObject::sender();
  int frame       = action->data().toInt();
  m_viewer->zoomToFramesPerPage(frame);
}

//-----------------------------------------------------------------------------

void LayerFooterPanel::onControlPressed(bool pressed) {
  isCtrlPressed = pressed;
  update();
}

//-----------------------------------------------------------------------------

const bool LayerFooterPanel::isControlPressed() { return isCtrlPressed; }
