/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
 * Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */

#include "articlewebpage.hh"
#include "articlewebview.hh"
#include "version.hh"
#include <QMouseEvent>
#include <QWebEngineScript>
#include <QWebEngineScriptCollection>
#include <QWebEngineView>
#include <QApplication>
#include <QTimer>
#include <QDialog>
#include <QMainWindow>
#ifdef Q_OS_WIN32
  #include <qt_windows.h>
#endif

ArticleWebView::ArticleWebView( QWidget * parent ):
  QWebEngineView( parent ),
  midButtonPressed( false ),
  selectionBySingleClick( false )
{
  auto page = new ArticleWebPage( this );
  connect( page, &ArticleWebPage::linkClicked, this, &ArticleWebView::linkClicked );
  this->setPage( page );

  QWebEngineScript script;
  script.setName( QStringLiteral( "GoldenDictInfo" ) );
  script.setSourceCode(
    QStringLiteral( "window.__DICT__ = { name: 'GoldenDict', version: '%1' };" ).arg( Version::version() ) );
  script.setInjectionPoint( QWebEngineScript::DocumentCreation );
  script.setWorldId( QWebEngineScript::MainWorld );
  script.setRunsOnSubFrames( true );
  page->scripts().insert( script );
}

void ArticleWebView::setUp( Config::Class * _cfg )
{
  this->cfg = _cfg;
  setZoomFactor( _cfg->preferences.zoomFactor );
}

void ArticleWebView::setPopup( bool isPopup )
{
  if ( auto page = qobject_cast< ArticleWebPage * >( this->page() ) ) {
    page->setPopup( isPopup );
  }
}

QWebEngineView * ArticleWebView::createWindow( QWebEnginePage::WebWindowType type )
{
  if ( type == QWebEnginePage::WebWindowType::WebDialog || type == QWebEnginePage::WebWindowType::WebBrowserTab
       || type == QWebEnginePage::WebWindowType::WebBrowserWindow ) {
    auto dlg  = new QMainWindow( this );
    auto view = new QWebEngineView( dlg );
    dlg->setAttribute( Qt::WA_DeleteOnClose );
    dlg->setObjectName( "ResourceViewer" );
    dlg->setCentralWidget( view );
    dlg->resize( 800, 600 );
    dlg->setWindowTitle( tr( "GoldenDict Resource Viewer" ) );
    dlg->show();

    return view;
  }
  return QWebEngineView::createWindow( type );
}

bool ArticleWebView::event( QEvent * event )
{
  if ( event->type() == QEvent::ChildAdded ) {
    auto child_ev = dynamic_cast< QChildEvent * >( event );
    child_ev->child()->installEventFilter( this );
  }

  return QWebEngineView::event( event );
}

void ArticleWebView::linkClickedInHtml( const QUrl & )
{
  //disable single click to simulate dbclick action on the new loaded pages.
  singleClickToDbClick = false;
}

bool ArticleWebView::eventFilter( QObject * obj, QEvent * ev )
{
  if ( ev->type() == QEvent::MouseButtonDblClick ) {
    auto pe = dynamic_cast< QMouseEvent * >( ev );
    if ( Qt::MouseEventSynthesizedByApplication != pe->source() ) {
      singleClickToDbClick = false;
      dbClicked            = true;
    }
  }
  if ( ev->type() == QEvent::MouseMove ) {
    singleClickToDbClick = false;
  }
  if ( ev->type() == QEvent::MouseButtonPress ) {
    auto pe = dynamic_cast< QMouseEvent * >( ev );
    if ( pe->button() == Qt::LeftButton ) {
      singleClickToDbClick = true;
      dbClicked            = false;
      QTimer::singleShot( QApplication::doubleClickInterval(), this, [ = ]() {
        singleClickAction();
      } );
    }
    if ( pe->buttons() & Qt::MiddleButton ) {
      midButtonPressed = true;
      QTimer::singleShot( 100, this, [ = ]() {
        sendCustomMouseEvent( QEvent::MouseButtonPress );
        sendCustomMouseEvent( QEvent::MouseButtonRelease );
      } );
      return false;
    }
  }
  if ( ev->type() == QEvent::MouseButtonRelease ) {
    auto pe = dynamic_cast< QMouseEvent * >( ev );
    mouseReleaseEvent( pe );
    if ( dbClicked ) {
      //emit the signal after button release.emit earlier(in MouseButtonDblClick event) can not get selected text;
      doubleClickAction( pe );
    }
  }
  if ( ev->type() == QEvent::Wheel ) {
    auto pe = dynamic_cast< QWheelEvent * >( ev );
    wheelEvent( pe );

    if ( pe->modifiers().testFlag( Qt::ControlModifier ) ) {
      return true;
    }
  }

  return QWebEngineView::eventFilter( obj, ev );
}

void ArticleWebView::singleClickAction()
{
  if ( !singleClickToDbClick ) {
    return;
  }

  if ( selectionBySingleClick ) {
    findText( "" ); // clear the selection first, if any
    //send dbl click event twice? send one time seems not work .weird really.  need further investigate.
    sendCustomMouseEvent( QEvent::MouseButtonDblClick );
    sendCustomMouseEvent( QEvent::MouseButtonDblClick );
  }
}

void ArticleWebView::sendCustomMouseEvent( QEvent::Type type )
{
  QPoint pt = mapFromGlobal( QCursor::pos() );
  QMouseEvent ev( type,
                  pt,
                  pt,
                  QCursor::pos(),
                  Qt::LeftButton,
                  Qt::LeftButton,
                  Qt::NoModifier,
                  Qt::MouseEventSynthesizedByApplication );

  auto childrens = this->children();
  for ( auto * child : childrens ) {
    QApplication::sendEvent( child, &ev );
  }
}

void ArticleWebView::doubleClickAction( QMouseEvent * event )
{
  if ( Qt::MouseEventSynthesizedByApplication != event->source() ) {
    emit doubleClicked( event->pos() );
  }
}

void ArticleWebView::wheelEvent( QWheelEvent * ev )
{
#ifdef Q_OS_WIN32

  // Avoid wrong mouse wheel handling in QWebEngineView
  // if system preferences is set to "scroll by page"

  if ( ev->modifiers() == Qt::NoModifier ) {
    unsigned nLines = 0;
    SystemParametersInfo( SPI_GETWHEELSCROLLLINES, 0, &nLines, 0 );
    if ( nLines == WHEEL_PAGESCROLL ) {
      QKeyEvent kev( QEvent::KeyPress, ev->angleDelta().y() > 0 ? Qt::Key_PageUp : Qt::Key_PageDown, Qt::NoModifier );
      auto childrens = this->children();
      for ( auto * child : childrens ) {
        QApplication::sendEvent( child, &kev );
      }

      ev->accept();
      return;
    }
  }
#endif

  if ( ev->modifiers().testFlag( Qt::ControlModifier ) ) {
    ev->ignore();
  }
  else {
    QWebEngineView::wheelEvent( ev );
  }
}
