/* 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
 */

#pragma once

/////////////////////// stdlib includes


/////////////////////// Qt includes
#include <QGraphicsView>
#include <QLineEdit>


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


/////////////////////// libXpertMass includes
#include <MsXpS/libXpertMassCore/CrossLinker.hpp>
#include <MsXpS/libXpertMassCore/IndexRangeCollection.hpp>
#include <MsXpS/libXpertMassCore/Polymer.hpp>


/////////////////////// libXpertMassGui includes


/////////////////////// Local includes
#include "ChemEntVignette.hpp"
#include "SequenceSelection.hpp"


namespace MsXpS
{
namespace MassXpert
{


enum MxtCursorLinePosition
{
  START_OF_LINE,
  END_OF_LINE
};

enum MxtCardinalDirection
{
  CENTER,
  NORTH,
  NORTH_EAST,
  EAST,
  SOUTH_EAST,
  SOUTH,
  SOUTH_WEST,
  WEST,
  NORTH_WEST
};


class SequenceEditorWnd;
class MonomerCodeEvaluator;
class CrossLinker;


class SequenceEditorGraphicsView : public QGraphicsView
{
  Q_OBJECT

  public:
  SequenceEditorGraphicsView(SequenceEditorWnd * = 0);
  ~SequenceEditorGraphicsView();

  bool m_kbdShiftDown;
  bool m_kbdCtrlDown;
  bool m_kbdAltDown;

  int requestedVignetteSize();
  int leftMargin();
  int columns();
  int rows();

  int lastClickedVignette();

  void setPolymer(libXpertMassCore::PolymerQSPtr polymer_sp);
  libXpertMassCore::PolymerQSPtr getPolymer();

  void setEditorWnd(SequenceEditorWnd *);

  void setMonomerCodeEvaluator(MonomerCodeEvaluator *evaluator);

  void updateColumns();
  void updateMonomerPosition(int);
  void updateVScrollBar();
  void updateSceneRect();

  int vignetteIndex(const QPointF &);
  QPointF vignetteLocation(std::size_t index,
                           MxtCardinalDirection = NORTH_WEST);

  void setSelection(std::size_t start,
                    std::size_t stop,
                    bool is_multi_region_selection,
                    bool is_multi_selection_region);
  void setSelection(const libXpertMassCore::IndexRange &index_range,
                    bool is_multi_region_selection,
                    bool is_multi_selection_region);
  void
  setSelection(const libXpertMassCore::IndexRangeCollection &index_range_collection,
               bool is_multi_region_selection,
               bool is_multi_selection_region);

  void resetSelection();
  void resetSelectionButLastRegion();
  void resetMultiSelectionRegionsButFirstSelection();

  bool
  selectionIndices(libXpertMassCore::IndexRangeCollection &index_range_collection);

  void setOngoingMouseMultiSelection(bool);

  // SCENE-DRAWING FUNCTIONS /////////////////////////////////////////////
  bool renderCursor();

  bool scaleCursor();

  bool positionCursor(MxtCursorLinePosition = START_OF_LINE);
  bool positionCursor(const QPointF &, MxtCursorLinePosition = START_OF_LINE);
  bool positionCursor(double, double, MxtCursorLinePosition = START_OF_LINE);
  bool positionCursor(std::size_t index, MxtCursorLinePosition = START_OF_LINE);
  bool positionCursor(const RegionSelection &,
                      MxtCursorLinePosition = START_OF_LINE);

  ChemEntVignetteSPtr
  renderMonomerVignette(const libXpertMassCore::MonomerCstSPtr monomer_csp,
                        const QString &uuid);

  bool modifyVignetteAt(std::size_t index,
                        const libXpertMassCore::Modif &modif,
                        const QString uuid);
  bool unmodifyVignetteAt(std::size_t index, const QString uuid);

  bool crossLinkVignetteAt(std::size_t index,
                           libXpertMassCore::CrossLinkCstSPtr cross_link_csp,
                           const QString uuid);
  bool uncrossLinkVignetteAt(std::size_t index, const QString uuid);
  bool removeMonomerVignetteAt(std::size_t index);
  bool renderAllMonomerVignettes();
  bool scaleVignette(ChemEntVignetteSPtr chem_ent_vignette_sp);
  bool scaleVignettes();
  bool positionVignette(ChemEntVignetteSPtr chem_ent_vignette_sp,
                        std::size_t index);
  bool positionVignettes();

  int drawSequence(bool = false);
  bool updateSequence();

  // POLYMER SEQUENCE-MODIFYING FUNCTIONS ///////////////////////////////
  bool insertMonomerAt(const libXpertMassCore::Monomer &monomer,
                       std::size_t index,
                       bool freeze = false);
  bool insertMonomerAtPoint(const libXpertMassCore::Monomer &monomer,
                            bool freeze = false);
  std::size_t insertSequenceAtPoint(libXpertMassCore::Sequence &sequence);

  bool removeMonomerAt(std::size_t index, bool freeze = false);
  bool prepareMonomerRemovalAt(std::size_t index);

  std::size_t removeSelectedOligomer();
  std::size_t removeSequenceRegion(std::size_t start, std::size_t stop);


  public slots:
  ////////////////////////////// SLOTS ///////////////////////////////
  void vScrollBarActionTriggered(int);
  bool requestVignetteSize(int);

  protected:
  // Unallocated, this pointer points to the m_polymer member of the
  // parent instance into which this graphics view packed. Usually
  // that is the SequenceEditorWnd sequence editor window were
  // this view is the m_editorView member.
  libXpertMassCore::PolymerQSPtr msp_polymer;
  SequenceEditorWnd *mp_editorWnd;
  MonomerCodeEvaluator *mpa_monomerCodeEvaluator;

  bool m_sequenceDrawn;

  int m_columns = 0;
  int m_rows    = 0;

  int m_lastClickedVignette = 0;

  SequenceSelection *mpa_selection;

  bool m_mouseDragging;
  bool m_ongoingMouseMultiSelection;
  bool m_ongoingKeyboardMultiSelection;

  QPointF m_selectionFirstPoint;
  QPointF m_selectionSecondPoint;

  int m_selectionFirstIndex  = 0;
  int m_selectionSecondIndex = 0;

  double m_xScaleFactor = 0;
  double m_yScaleFactor = 0;

  int m_leftMargin = 0;

  int m_requestedVignetteSize = 0; // the width in fact.

  QSvgRenderer *mpa_cursorRenderer;
  QGraphicsSvgItem *mpa_cursorVignette;

  std::vector<ChemEntVignetteSPtr> m_monomerVignettes;

  // This list will hold pointers to allocated QGraphicsTextItem
  // items, but we do not possess these. The possessor of these items
  // is the scene that is a member of the sequence editor window. We
  // have this list that somehow duplicates the internal scene list
  //(that is returned by the items() call) because we needed to be
  // able to access the text items apart from all the other
  // items. Indeed, these items are the text labels that are displayed
  // in the left margin area to indicate the position of the monomer
  // in the polymer sequence at each beginning of scene line.
  QList<QGraphicsTextItem *> m_labelList;

  void focusOutEvent(QFocusEvent *event);

  void paintEvent(QPaintEvent *event);
  void resizeEvent(QResizeEvent *event);

  void mouseMoveEvent(QMouseEvent *event);
  void mousePressEvent(QMouseEvent *event);
  void mouseReleaseEvent(QMouseEvent *event);

  void keyPressEvent(QKeyEvent *event);
  void keyReleaseEvent(QKeyEvent *event);

  // KEYBOARD HANDLERS ///////////////////////////////////////////////////
  void MoveToPreviousChar(QKeyEvent *event);
  void MoveToNextChar(QKeyEvent *event);
  void MoveToPreviousLine(QKeyEvent *event);
  void MoveToNextLine(QKeyEvent *event);
  void MoveToPreviousPage(QKeyEvent *event);
  void MoveToNextPage(QKeyEvent *event);
  void MoveToStartOfLine(QKeyEvent *event);
  void MoveToEndOfLine(QKeyEvent *event);
  void MoveToStartOfDocument(QKeyEvent *event);
  void MoveToEndOfDocument(QKeyEvent *event);
  void SelectNextChar(QKeyEvent *event);
  void SelectPreviousChar(QKeyEvent *event);
  void SelectNextLine(QKeyEvent *event);
  void SelectPreviousLine(QKeyEvent *event);
  void SelectNextPage(QKeyEvent *event);
  void SelectPreviousPage(QKeyEvent *event);
  void SelectStartOfLine(QKeyEvent *event);
  void SelectEndOfLine(QKeyEvent *event);
  void SelectStartOfDocument(QKeyEvent *event);
  void SelectEndOfDocument(QKeyEvent *event);
  void SelectAll(QKeyEvent *event);

  void keyPressEventKeyEscape(QKeyEvent *event);
  void keyPressEventKeyReturn(QKeyEvent *event);
  void keyPressEventKeyBackspace(QKeyEvent *event);
  void keyPressEventKeyDelete(QKeyEvent *event);
  void keyPressEventAlpha(QKeyEvent *event);
};


} // namespace MassXpert
} // namespace MsXpS
