/* BEGIN software license
 *
 * MsXpertSuite - mass spectrometry software suite
 * -----------------------------------------------
 * Copyright(C) 2009,...,2026 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();

  libXpertMassCore::IndexRangeCollection index_range_collection;

  bool selectionPresent = selectionIndices(index_range_collection);

  if(index_range_collection.size() > 1)
    {
      // libXpertMassCore::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)
{
  libXpertMassCore::IndexRangeCollection index_range_collection;

  bool selectionPresent = selectionIndices(index_range_collection);

  if(index_range_collection.size() > 1)
    {
      // libXpertMassCore::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 >= static_cast<int>(msp_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();

  libXpertMassCore::IndexRangeCollection index_range_collection;

  selectionIndices(index_range_collection);

  if(index_range_collection.size() > 1)
    {
      // libXpertMassCore::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;
    }
  QString text = event->text();
  mpa_monomerCodeEvaluator->newKey(text);

  return event->accept();
}

} // namespace MassXpert

} // namespace MsXpS
