/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright 2009--2026 by Filippo Rusconi
 *
 * http://www.msxpertsuite.org
 *
 * This file is part of the MsXpertSuite project.
 *
 * The MsXpertSuite project is the successor of the massXpert project. This
 * project now includes various independent modules:
 *
 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
 *
 * 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/>.
 *
 * END software license
 */


/////////////////////// Qt includes
#include <QClipboard>
#include <QScreen>
#include <QMouseEvent>
#include <QTextDocument>
#include <QTimer>
#include <QLabel>


/////////////////////// pappsomspp includes


/////////////////////// Local includes
#include "MsXpS/libXpertMassGui/TextDetailsWidget.hpp"

namespace MsXpS
{
namespace libXpertMassGui
{

TextDetailsWidget::TextDetailsWidget(const QFont &font, QWidget *parent)
  : QWidget(parent)
{
  // Window flags: Make it a frameless, always-on-top tooltip
  setWindowFlags(Qt::ToolTip | Qt::FramelessWindowHint |
                 Qt::WindowStaysOnTopHint | Qt::WindowDoesNotAcceptFocus);
  // setAttribute(Qt::WA_TranslucentBackground);
  setAttribute(Qt::WA_ShowWithoutActivating);

  // CRITICAL: Make the widget transparent to mouse events!
  // This ensures that when the tooltip pops up under the cursor,
  // the mouse events still pass through to the QCustomPlot underneath.
  // No, no more because we want to allow the user to click the copy
  // push button.
  // setAttribute(Qt::WA_TransparentForMouseEvents, true);

  // Layout
  QVBoxLayout *mp_mainVerticalLayout = new QVBoxLayout(this);
  mp_mainVerticalLayout->setContentsMargins(
    0, 0, 0, 0); // No margins, let QTextEdit handle it
  mp_mainVerticalLayout->setSpacing(0);

  // The multiline edit widget
  mp_textEdit = new QTextEdit(this);
  mp_textEdit->setReadOnly(true); // Prevent editing, but allow selection
  mp_textEdit->setFrameStyle(QFrame::NoFrame); // Remove default borders
  mp_textEdit->setFrameShape(QFrame::NoFrame);
  mp_textEdit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  mp_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
  mp_textEdit->viewport()->setAutoFillBackground(false);
  mp_textEdit->setFont(font);

  mp_mainVerticalLayout->addWidget(mp_textEdit);

  // We now want to add a horizontal layout where to pack buttons.
  mp_buttonsRowHorizontalLayout = new QHBoxLayout(this);
  mp_mainVerticalLayout->addLayout(mp_buttonsRowHorizontalLayout);

  // The default copy-to-clipboard button. Mind the setting up of the button so
  // that when clicked the button icon transitorially changes to a check mark
  // icon and then back to the initial button icon.
  mp_copyTextButton = new QPushButton(this);
  // Make checkable to ease the icon management.
  mp_copyTextButton->setCheckable(true);

  QIcon button_icon;
  // When QIcon::Off, that means the button has not be clicked, and then
  // that means that we want the copy text to clipboard icon.
  button_icon.addFile(
    ":copy-text-to-clipboard-icon.svg", QSize(), QIcon::Normal, QIcon::Off);

  // When QIcon::On, that means the button has been clicked: show
  // temporaneously the check mark icon to tell the user that the
  // button has been clicked and that the text has been copied (see
  // copyTextToClipboard().
  button_icon.addFile(
    ":black-check-mark-icon.svg", QSize(), QIcon::Normal, QIcon::On);

  mp_copyTextButton->setChecked(false);

  mp_copyTextButton->setIcon(button_icon);
  mp_copyTextButton->setIconSize(QSize(24, 24));
  mp_copyTextButton->setToolTip("Copy text to clipboard");
  mp_copyTextButton->setFlat(true);
  mp_copyTextButton->setCursor(Qt::PointingHandCursor);
  mp_copyTextButton->setFixedSize(26, 26);
  connect(mp_copyTextButton,
          &QPushButton::clicked,
          this,
          &TextDetailsWidget::copyTextToClipboard);

  mp_buttonsRowHorizontalLayout->addWidget(
    mp_copyTextButton, 0, Qt::AlignRight | Qt::AlignBottom);

  // Now the fake status bar widget

  // First a nice separator line
  QFrame *separator_line_p = new QFrame();
  separator_line_p->setFrameShape(QFrame::HLine);
  separator_line_p->setFrameShadow(QFrame::Sunken);
  mp_mainVerticalLayout->addWidget(separator_line_p);

  // The line edit widget.
  // --- Custom Status Bar Setup ---
  QFrame *status_bar_frame_p = new QFrame();
  status_bar_frame_p->setFrameShape(QFrame::StyledPanel);
  status_bar_frame_p->setFrameShadow(QFrame::Sunken);

  QHBoxLayout *statusLayout = new QHBoxLayout(status_bar_frame_p);
  statusLayout->setContentsMargins(4, 2, 4, 2);

  mp_statusBarLabel = new QLabel("");
  statusLayout->addWidget(mp_statusBarLabel);
  statusLayout->addStretch();

  mp_mainVerticalLayout->addWidget(status_bar_frame_p);

  // Timer for auto-clearing messages
  mp_statusBarTimer = new QTimer(this);

  // --- Styling ---
  setStyleSheet(R"(
        TextDetailsWidget {
            background-color: #dcfaff; 
            border: 1px solid #3F3F46;
            border-radius: 6px;
        }
        QTextEdit {
            // background: transparent;
            background-color: #889dda;
            color: #88cad6;
            border: none;
            padding: 0;
            font-size: 13px;
            selection-background-color: #007ACC;
            selection-color: #FFFFFF;
        }
        QPushButton {
            background-color: #889dda;
            border: 1px solid transparent;
            border-radius: 4px;
        }
        QPushButton:hover {
            background-color: #889dda;
            border: 1px solid #555555;
        }
        QPushButton:pressed {
            background-color: #9e88da;
        }
    )");

  // Auto-hide timer
  mp_hideTimer = new QTimer(this);
  mp_hideTimer->setSingleShot(true);
  connect(mp_hideTimer, &QTimer::timeout, this, &TextDetailsWidget::hideWidget);

  qApp->installEventFilter(this);
  hide();
}

TextDetailsWidget::TextDetailsWidget(const QFont &font,
                                     const QString &default_status_bar_message,
                                     QWidget *parent)
  : TextDetailsWidget(font, parent)
{
  m_defaultStatusBarMessage = default_status_bar_message;
  showStatusBarMessage(m_defaultStatusBarMessage, -1);
}

TextDetailsWidget::~TextDetailsWidget()
{
}

void
TextDetailsWidget::showText(const QString &text,
                            const QPoint &globalMousePos,
                            bool is_html)
{
  if(is_html)
    mp_textEdit->setHtml(text);
  else
    mp_textEdit->setPlainText(text);

  int targetWidth = 260;
  mp_textEdit->document()->setTextWidth(targetWidth);
  QSizeF doc_size_f = mp_textEdit->document()->size();
  QSize doc_size    = doc_size_f.toSize();

  int margin           = 20; // 10px padding on all sides
  int buttonAreaHeight = 36; // 28px button + 8px spacing

  int totalWidth  = doc_size.width() + margin;
  int totalHeight = doc_size.height() + margin + buttonAreaHeight;

  // Prevent the widget from taking up the entire screen vertically
  int maxHeight =
    QApplication::primaryScreen()->availableGeometry().height() * 0.75;
  if(totalHeight > maxHeight)
    {
      totalHeight = maxHeight;
      mp_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    }
  else
    {
      mp_textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    }

  setFixedSize(totalWidth, totalHeight);

  // --- Positioning ---
  QPoint finalPos  = globalMousePos + QPoint(15, 15);
  QRect screenGeom = QApplication::primaryScreen()->availableGeometry();

  if(finalPos.x() + width() > screenGeom.right())
    {
      finalPos.setX(globalMousePos.x() - width() - 15);
    }
  if(finalPos.y() + height() > screenGeom.bottom())
    {
      finalPos.setY(globalMousePos.y() - height() - 15);
    }

  move(finalPos);
  show();
  raise();

  startHideTimer();
}

void
TextDetailsWidget::addButton(QPushButton *button_p, bool prepend)
{
  Q_ASSERT(button_p != nullptr);

  // Now reparent the button.
  button_p->setParent(this);

  // connect(button_p, &QPushButton::clicked, this, [button_p, this]() {
  //   button_p->setChecked(true);

  //   QTimer::singleShot(1000, this, [button_p]() {
  //     button_p->setChecked(false);
  //   });
  // });

  if(prepend)
    mp_buttonsRowHorizontalLayout->insertWidget(0, button_p);
  else
    mp_buttonsRowHorizontalLayout->addWidget(button_p);
}

void
TextDetailsWidget::forcefullyHide()
{
  hideWidget();
}

void
TextDetailsWidget::setDefaultStatusBarMessage(const QString &message)
{
  m_defaultStatusBarMessage = message;
}

void
TextDetailsWidget::showStatusBarMessage(const QString &message, int time_out)
{
  mp_statusBarLabel->setText(message);

  if(mp_statusBarTimer->isActive())
    mp_statusBarTimer->stop();

  if(time_out > 0)
    {
      connect(mp_statusBarTimer, &QTimer::timeout, this, [this]() {
        mp_statusBarLabel->setText(m_defaultStatusBarMessage);
      });

      mp_statusBarTimer->setSingleShot(true);
      mp_statusBarTimer->setInterval(2000);
      mp_statusBarTimer->start();
    }
}

void
TextDetailsWidget::setPinned(bool pinned)
{
  m_isPinned = pinned;

  if(m_isPinned)
    {
      // qDebug() << "stopping the hide timer";
      mp_hideTimer->stop();
    }
  else
    {
      // qDebug() << "starting the hide timer";
      mp_hideTimer->start();
    }
}

void
TextDetailsWidget::copyTextToClipboard()
{
  // We copy the plain text so the user doesn't get raw HTML tags in their
  // clipboard

  // qDebug() << "The text is:" << mp_textEdit->toPlainText();

  QApplication::clipboard()->setText(mp_textEdit->toPlainText());

  mp_copyTextButton->setChecked(true);

  QTimer::singleShot(1000, this, [this]() {
    mp_copyTextButton->setChecked(false);
  });
}

void
TextDetailsWidget::enterEvent(QEnterEvent *event)
{
  // qDebug() << "";

  mp_hideTimer->stop();
  QWidget::enterEvent(event);
}

void
TextDetailsWidget::leaveEvent(QEvent *event)
{
  // qDebug() << "";

  startHideTimer();
  QWidget::leaveEvent(event);
}

bool
TextDetailsWidget::eventFilter(QObject *obj, QEvent *event)
{
  // qDebug() << "";

  if(event->type() == QEvent::MouseButtonPress)
    {
      if(isVisible() && !geometry().contains(QCursor::pos()))
        {
          hideWidget();
        }
    }
  return QWidget::eventFilter(obj, event);
}

void
TextDetailsWidget::startHideTimer()
{
  // qDebug() << "";

  // Right enough time to be able to release the mouse, go on the
  // widget area and click the copy-to-clipboard button.
  mp_hideTimer->start(500); // these are milliseconds
}

void
TextDetailsWidget::hideWidget()
{
  m_isPinned ? show() : hide();
}

} // namespace libXpertMassGui
} // namespace MsXpS
