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


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


namespace MsXpS
{

namespace MassXpert
{


SequenceSelection::SequenceSelection(SequenceEditorGraphicsView *view)
  : mp_view(view)
{
}


SequenceSelection::~SequenceSelection()
{
}


void
SequenceSelection::setView(SequenceEditorGraphicsView *view_p)
{
  mp_view = view_p;
}


SequenceEditorGraphicsView *
SequenceSelection::getView()
{
  return mp_view;
}


const std::vector<RegionSelectionSPtr> &
SequenceSelection::getRegionSelectionsCstRef() const
{
  return m_regionSelections;
}

std::vector<RegionSelectionSPtr> &
SequenceSelection::getRegionSelectionsRef()
{
  return m_regionSelections;
}

int
SequenceSelection::selectRegion(const QPointF &start_point,
                                const QPointF &stop_point,
                                bool multi_region_selection,
                                bool multi_selection_region)
{
  // We are asked to create a region selection encompassing the two
  // points passed as parameter.

  if(!isManhattanLength(start_point, stop_point))
    {
      // 	qDebug() << __FILE__ << __LINE__
      // 		  << "selectRegion: isManhattanLength failed with:"
      // 		  << start_point << stop_point;

      return 0;
    }

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "selectRegion with params:"
  // 	      << start_point << stop_point;

  if(!multi_region_selection)
    {
      // multi_region_selection: there can be more than one region
      // selected in the sequence.

      // The caller asks that there cannot be more than one selected
      // region at any given time in the sequence. Thus we first
      // eliminate any previously existing region selection.

      deselectRegions();
    }

  if(!multi_selection_region)
    {
      // multi_selection_region: one region in the sequence might be
      // selected more than once.

      // The caller asks that if some regionSelection already
      // encompasses the region we are about to select, then we
      // first should remove it.

      // Start searching in the member container at index = 0.
      std::size_t index = 0;
      RegionSelectionSPtr region_selection_sp =
        regionSelectionEncompassing(start_point, stop_point, index);

      if(region_selection_sp)
        {
          // 	    qDebug() << __FILE__ << __LINE__
          // 	      << "selectRegion: delectRegion found encompassing:"
          // 		      << region_selection_sp;

          deselectRegion(region_selection_sp);
        }
    }

  // At this point we know we can select the region.
  RegionSelectionSPtr region_selection_sp =
    std::make_shared<RegionSelection>(mp_view);

  // Returns the number of actual selection rectangle graphics items
  // ware created to draw the whole selection mark.
  int result = region_selection_sp->drawMark(start_point, stop_point);

  if(result < 1)
    {
      region_selection_sp.reset();
      return result;
    }

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "selectRegion indices:" << selection
  // 	      << "[" << selection->startIndex() << "--"
  // 	      << selection->endIndex() << "]";

  m_regionSelections.push_back(region_selection_sp);

  //     qDebug() <<__FILE__ << __LINE__
  // 	      << "selectRegion count:" <<m_regionSelections.size();

  return result;
}


int
SequenceSelection::selectRegion(int start_index,
                                int stop_index,
                                bool multi_region_selection,
                                bool multi_selection_region)
{
  // We are asked to create a region selection encompassing the two
  // indices passed as parameter.

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "selectRegion with params:"
  // 	      << start_index << stop_index;

  if(!multi_region_selection)
    {
      // multi_region_selection: there can be more than one region
      // selected in the sequence.

      // The caller asks that there cannot be more than one selected
      // region at any given time in the sequence. Thus we first
      // eliminate any previously existing region selection.

      deselectRegions();
    }

  if(!multi_selection_region)
    {
      // multi_selection_region: one region in the sequence might be
      // selected more than once.

      // The caller asks that if some regionSelection already
      // encompasses the region we are about to select, then we
      // first should remove it.

      // Start searching in the member container at index = 0.
      std::size_t index = 0;

      RegionSelectionSPtr region_selection_sp =
        regionSelectionEncompassing(start_index, stop_index, index);

      if(region_selection_sp != nullptr)
        {
          // 	    qDebug() << __FILE__ << __LINE__
          // 	      << "selectRegion: delectRegion found encompassing:"
          // 		      << region_selection_sp;

          deselectRegion(region_selection_sp);
        }
    }

  // At this point we know we can select the region.
  RegionSelectionSPtr region_selection_sp =
    std::make_shared<RegionSelection>(mp_view);

  // Returns the number of actual selection rectangle graphics items
  // ware created to draw the whole selection mark.
  int result = region_selection_sp->drawMark(start_index, stop_index);

  if(result < 1)
    {
      region_selection_sp.reset();
      return result;
    }

  m_regionSelections.push_back(region_selection_sp);

  return result;
}


void
SequenceSelection::deselectRegions()
{
  m_regionSelections.clear();
}


int
SequenceSelection::reselectRegions()
{
  for(const RegionSelectionSPtr &region_selection_sp : m_regionSelections)
    region_selection_sp->redrawMark();

  return m_regionSelections.size();
}


bool
SequenceSelection::deselectRegion(RegionSelectionSPtr region_selection_sp)
{
  std::vector<RegionSelectionSPtr>::iterator the_iterator = std::find_if(
    m_regionSelections.begin(),
    m_regionSelections.end(),
    [region_selection_sp](RegionSelectionSPtr &iter_region_selection_sp) {
      return iter_region_selection_sp = region_selection_sp;
    });

  if(the_iterator != m_regionSelections.end())
    {
      m_regionSelections.erase(the_iterator);
      return true;
    }

  return false;
}


bool
SequenceSelection::deselectLastRegion()
{
  if(!m_regionSelections.size())
    return false;

  m_regionSelections.erase(std::prev(m_regionSelections.end()));

  return true;
}


bool
SequenceSelection::deselectRegionsButLast()
{
  if(m_regionSelections.size() < 2)
    return false;

  m_regionSelections.erase(m_regionSelections.begin(),
                           std::prev(m_regionSelections.end()));

  return true;
}

int
SequenceSelection::deselectMultiSelectionRegionsButFirstSelection()
{
  // If any region is selected more than once, then remove all the
  // selections but the first one. The result is that there might be
  // more than one selected region in the sequence, BUT each region
  // is selected only once.

  std::vector<RegionSelectionSPtr>::iterator the_iterator =
    m_regionSelections.begin();

  std::size_t index = 0;

  while(the_iterator != m_regionSelections.end())
    {
      // Get what is becoming the reference RegionSelection
      RegionSelectionSPtr reference_region_selection_sp = (*the_iterator);

      // If we are iterating in the last RegionSelection, nothing to do.
      if(std::next(the_iterator) == m_regionSelections.end())
        return 1;

      // Because we know we are not in the last RegionSelection of the
      // container, we can continue looking down the container. Find if there is
      // another RegionSelection that encompasses the same indices.

      auto the_lambda = [&](RegionSelectionSPtr reference_region_selection_sp) {
        RegionSelectionSPtr region_selection_sp = regionSelectionEncompassing(
          reference_region_selection_sp->getStartIndex(),
          reference_region_selection_sp->getStopIndex(),
          index);
        return region_selection_sp != nullptr ? 1 : 0;
      };

      m_regionSelections.erase(std::remove_if(std::next(the_iterator),
                                              m_regionSelections.end(),
                                              the_lambda),
                               m_regionSelections.end());

      ++the_iterator;
    }
    //  End of
    // while(the_iterator_cst != the_end_iterator_cst)

#ifdef Q_DEBUG
  for(RegionSelectionSPtr &region_selection_sp : m_regionSelections)
    qDebug() << "One RegionSelection:"
             << region_selection_sp->getIndicesAsText();
#endif

  return 1;
}


RegionSelectionSPtr
SequenceSelection::regionSelectionEncompassing(int start_index,
                                               int stop_index,
                                               std::size_t &index)
{
  // Return the region selection that contains the vignettes between indices
  // 'start_index' and 'stop_index'.

  // Start the search in the member container of region selections at item
  // located at index 'index'.

  // Return the index of the found RegionSelection instance in the member
  // container.

  if(index >= m_regionSelections.size())
    return nullptr;

  std::vector<RegionSelectionSPtr>::const_iterator the_begin_iterator_cst =
    m_regionSelections.cbegin();
  std::vector<RegionSelectionSPtr>::const_iterator the_iterator_cst =
    m_regionSelections.cbegin() + index;
  std::vector<RegionSelectionSPtr>::const_iterator the_end_iterator_cst =
    m_regionSelections.cend();

  while(the_iterator_cst != the_end_iterator_cst)
    {
      if((*the_iterator_cst)->encompassing(start_index, stop_index))
        {
          index = std::distance(the_begin_iterator_cst, the_iterator_cst);
          return *the_iterator_cst;
        }

      ++the_iterator_cst;
    }

  return nullptr;
}


RegionSelectionSPtr
SequenceSelection::regionSelectionEncompassing(const QPointF &start_point,
                                               const QPointF &stop_point,
                                               std::size_t &index)
{
  // Because we get QPointF data, the secondIndex that will be
  // computed below will be one unit greater than the formal last
  // index of the selection. Thus, we first sort the computed
  // values, so as to know which index is for the end of the
  // selection, and we decrement it by one.

  int start_index = mp_view->vignetteIndex(start_point);
  int stop_index  = mp_view->vignetteIndex(stop_point);

  qDebug() << "regionSelectionEncompassing:" << start_point << stop_point
           << "giving indices:" << start_index << "--" << stop_index;

  if(start_index > stop_index)
    {
      std::swap(start_index, stop_index);

      qDebug() << "After swap, regionSelectionEncompassing:" << start_point
               << stop_point << "giving indices:" << start_index << "--"
               << stop_index;
    }

  // The line below seems faulty as it makes it impossible to select
  // to contigyous regions because the previous region would always
  // be found overlapping to the new due to the stop_index - 1
  // below.

  //     return regionSelectionEncompassing(start_index, stop_index - 1,
  // 					listIndex);

  return regionSelectionEncompassing(start_index, stop_index, index);
}


double
SequenceSelection::manhattanLength()
{
  return mp_view->requestedVignetteSize() / 0.3;
}


bool
SequenceSelection::isManhattanLength(const QPointF &start_point,
                                     const QPointF &stop_point)
{
  double manhattan          = manhattanLength();
  int requestedVignetteSize = mp_view->requestedVignetteSize();

  if(abs(static_cast<int>(start_point.x() - stop_point.x())) <
       requestedVignetteSize / manhattan &&
     abs(static_cast<int>(start_point.y() - stop_point.y())) <
       requestedVignetteSize / manhattan)
    {
      return false;
    }

  return true;
}


int
SequenceSelection::regionSelectionCount()
{
  return m_regionSelections.size();
}


bool
SequenceSelection::selectionIndices(
  libXpertMassCore::IndexRangeCollection &index_range_collection)
{
  index_range_collection.clear();

  for(const RegionSelectionSPtr &region_selection_sp : m_regionSelections)
    {
      index_range_collection.getRangesRef().push_back(
        new libXpertMassCore::IndexRange(region_selection_sp->getIndexRange(),
                                     &index_range_collection));
    }

  return index_range_collection.size() ? true : false;
}


} // namespace MassXpert
} // namespace MsXpS
