/* BEGIN software license
 *
 * msXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2018 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 <QMessageBox>


/////////////////////// Local includes
#include "SequenceEditorGraphicsViewKeyboardHandling.hpp"
#include "SequenceEditorGraphicsViewKeySequenceHandling.hpp"


namespace MsXpS
{

namespace massxpert
{


  void
  SequenceEditorGraphicsView::keyPressEvent(QKeyEvent *event)
  {
    int keyCode                     = event->key();
    QString text                    = event->text();
    Qt::KeyboardModifiers modifiers = event->modifiers();


    if(modifiers & Qt::ShiftModifier)
      m_kbdShiftDown = true;

    if(modifiers & Qt::ControlModifier)
      m_kbdCtrlDown = true;

    if(modifiers & Qt::AltModifier)
      m_kbdAltDown = true;

    if(event->matches(QKeySequence::MoveToPreviousChar))
      return MoveToPreviousChar(event);

    if(event->matches(QKeySequence::MoveToNextChar))
      return MoveToNextChar(event);

    if(event->matches(QKeySequence::MoveToPreviousLine))
      return MoveToPreviousLine(event);

    if(event->matches(QKeySequence::MoveToNextLine))
      return MoveToNextLine(event);

    if(event->matches(QKeySequence::MoveToPreviousPage))
      return MoveToPreviousPage(event);

    if(event->matches(QKeySequence::MoveToNextPage))
      return MoveToNextPage(event);

    if(event->matches(QKeySequence::MoveToStartOfLine))
      return MoveToStartOfLine(event);

    if(event->matches(QKeySequence::MoveToEndOfLine))
      return MoveToEndOfLine(event);

    if(event->matches(QKeySequence::MoveToStartOfDocument))
      return MoveToStartOfDocument(event);

    if(event->matches(QKeySequence::MoveToEndOfDocument))
      return MoveToEndOfDocument(event);

    if(event->matches(QKeySequence::SelectNextChar))
      return SelectNextChar(event);

    if(event->matches(QKeySequence::SelectPreviousChar))
      return SelectPreviousChar(event);

    if(event->matches(QKeySequence::SelectNextLine))
      return SelectNextLine(event);

    if(event->matches(QKeySequence::SelectPreviousLine))
      return SelectPreviousLine(event);

    if(event->matches(QKeySequence::SelectNextPage))
      return SelectNextPage(event);

    if(event->matches(QKeySequence::SelectPreviousPage))
      return SelectPreviousPage(event);

    if(event->matches(QKeySequence::SelectStartOfLine))
      return SelectStartOfLine(event);

    if(event->matches(QKeySequence::SelectEndOfLine))
      return SelectEndOfLine(event);

    if(event->matches(QKeySequence::SelectStartOfDocument))
      return SelectStartOfDocument(event);

    if(event->matches(QKeySequence::SelectEndOfDocument))
      return SelectEndOfDocument(event);

    if(event->matches(QKeySequence::SelectAll))
      return SelectAll(event);

    switch(keyCode)
      {
        case Qt::Key_Left:
          MoveToPreviousChar(event);
          break;

        case Qt::Key_Right:
          MoveToNextChar(event);
          break;

        case Qt::Key_Up:
          MoveToPreviousLine(event);
          break;

        case Qt::Key_Down:
          MoveToNextLine(event);
          break;

        case Qt::Key_PageUp:
          MoveToPreviousPage(event);
          break;

        case Qt::Key_PageDown:
          MoveToNextPage(event);
          break;

        case Qt::Key_Escape:
          return keyPressEventKeyEscape(event);
          break;

        case Qt::Key_Return:
          return keyPressEventKeyReturn(event);
          break;

        case Qt::Key_Enter:
          return keyPressEventKeyReturn(event);
          break;

        case Qt::Key_Backspace:
          return keyPressEventKeyBackspace(event);
          break;

        case Qt::Key_Delete:
          return keyPressEventKeyDelete(event);
          break;
      }

    if(!text.isEmpty())
      {
        if(text.length() > 1)
          return;

        if(text.at(0).category() != QChar::Letter_Uppercase &&
           text.at(0).category() != QChar::Letter_Lowercase)
          return;

        return keyPressEventAlpha(event);
      }

    return event->ignore();

    //   qDebug() << " keyPressEvent " << "text:" << text.toAscii()
    // 	    << "code:" << keyCode;
  }


  void
  SequenceEditorGraphicsView::keyReleaseEvent(QKeyEvent *event)
  {
    //  qDebug() << "keyReleaseEvent";

    Qt::KeyboardModifiers modifiers = event->modifiers();

    if(!(modifiers & Qt::ShiftModifier))
      {
        m_kbdShiftDown                  = false;
        m_ongoingKeyboardMultiSelection = false;
      }

    if(!(modifiers & Qt::ControlModifier))
      m_kbdCtrlDown = false;

    if(!(modifiers & Qt::AltModifier))
      m_kbdAltDown = false;

    return event->accept();
  }


  void
  SequenceEditorGraphicsView::keyPressEventKeyEscape(QKeyEvent *event)
  {
    mpa_monomerCodeEvaluator->escapeKey();

    return event->accept();
  }


  void
  SequenceEditorGraphicsView::keyPressEventKeyReturn(QKeyEvent *event)
  {
    mpa_monomerCodeEvaluator->reportCompletions();

    return event->accept();
  }


  void
  SequenceEditorGraphicsView::keyPressEventKeyBackspace(QKeyEvent *event)
  {
    QScrollBar *vScrollBar = verticalScrollBar();
    int curPos             = vScrollBar->sliderPosition();

    libXpertMass::CoordinateList coordList;

    bool selectionPresent = selectionIndices(&coordList);

    if(coordList.size() > 1)
      {
        // libXpertMass::Sequence editing can only occur when there is at most one
        // region selection.
        QMessageBox::information(this,
                                 tr("massxpert"),
                                 tr("Sequence editing with multi-region "
                                    "selection is not supported."),
                                 QMessageBox::Ok);
        return;
      }

    if(selectionPresent)
      {
        int count = removeSelectedOligomer();
        if(!count)
          qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

        // Remove all the region selections, actually, there is only
        // one, so no risk whatsoever.
        mpa_selection->deselectRegions();

        updateSequence();

        vScrollBar->setSliderPosition(curPos);

        QPointF point =
          mapFromScene(vignetteLocation(m_lastClickedVignette, CENTER));

        if(point.y() <= (m_requestedVignetteSize / 4))
          vScrollBarActionTriggered(QAbstractSlider::SliderSingleStepSub);

        //       qDebug() << "keyPressEventKeyBackspace"
        // 		<< "Removed" << count << "oligomers"
        // 		<< "m_lastClickedVignette:" << m_lastClickedVignette
        // 		<< "selectionFirstIndex:" << m_selectionFirstIndex
        // 		<< "selectionSecondIndex:" << m_selectionSecondIndex
        // 		<< "m_selectionFirstPoint" << m_selectionFirstPoint
        // 		<< "m_selectionSecondPoint" << m_selectionSecondPoint;

        return event->accept();
      }

    if(m_lastClickedVignette <= 0)
      return;

    removeMonomerAt(m_lastClickedVignette - 1);

    --m_lastClickedVignette;

    updateSequence();

    vScrollBar->setSliderPosition(curPos);

    QPointF point =
      mapFromScene(vignetteLocation(m_lastClickedVignette, CENTER));

    if(point.y() <= (m_requestedVignetteSize / 4))
      vScrollBarActionTriggered(QAbstractSlider::SliderSingleStepSub);

    //   qDebug() << "keyPressEventKeyBackspace"
    // 	    << "m_lastClickedVignette:" << m_lastClickedVignette
    // 	    << "selectionFirstIndex:" << m_selectionFirstIndex
    // 	    << "selectionSecondIndex:" << m_selectionSecondIndex
    // 	    << "m_selectionFirstPoint" << m_selectionFirstPoint
    // 	    << "m_selectionSecondPoint" << m_selectionSecondPoint;

    return event->accept();
  }


  void
  SequenceEditorGraphicsView::keyPressEventKeyDelete(QKeyEvent *event)
  {
    libXpertMass::CoordinateList coordList;

    bool selectionPresent = selectionIndices(&coordList);

    if(coordList.size() > 1)
      {
        // libXpertMass::Sequence editing can only occur when there is at most one
        // region selection.
        QMessageBox::information(this,
                                 tr("massxpert"),
                                 tr("Sequence editing with multi-region "
                                    "selection is not supported."),
                                 QMessageBox::Ok);
        return;
      }

    if(selectionPresent)
      {
        int count = removeSelectedOligomer();
        if(!count)
          qFatal("Fatal error at %s@%d. Program aborted.", __FILE__, __LINE__);

        // Remove all the region selections, actually, there is only
        // one, so no risk whatsoever.
        mpa_selection->deselectRegions();

        updateSequence();

        QPointF pointF = vignetteLocation(m_lastClickedVignette);
        centerOn(pointF);

        return event->accept();
      }

    if(m_lastClickedVignette >= mp_polymer->size())
      return;

    removeMonomerAt(m_lastClickedVignette);

    updateSequence();

    QPointF pointF = vignetteLocation(m_lastClickedVignette);
    centerOn(pointF);

    return event->accept();
  }


  void
  SequenceEditorGraphicsView::keyPressEventAlpha(QKeyEvent *event)
  {
    //   qDebug() << "Entering keyPressEventAlpha with text:"
    // 	    << event->text();

    libXpertMass::CoordinateList coordList;

    selectionIndices(&coordList);

    if(coordList.size() > 1)
      {
        // libXpertMass::Sequence editing can only occur when there is at most one
        // region selection.
        QMessageBox::information(this,
                                 tr("massxpert"),
                                 tr("Sequence editing with multi-region "
                                    "selection is not supported."),
                                 QMessageBox::Ok);
        return;
      }

    mpa_monomerCodeEvaluator->newKey(event->text());

    return event->accept();
  }

} // namespace massxpert

} // namespace MsXpS
