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


/////////////////////// Local includes
#include <massxpert/gui/SequenceSelection.hpp>
#include <massxpert/gui/SequenceEditorGraphicsView.hpp>


namespace msXpSmassXpert
{


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


SequenceSelection::~SequenceSelection()
{
  while(!m_regionSelectionList.isEmpty())
    delete m_regionSelectionList.takeFirst();
}


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


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


const QList<RegionSelection *> &
SequenceSelection::regionSelectionList() const
{
  return m_regionSelectionList;
}

int
SequenceSelection::selectRegion(const QPointF &point1,
                                const QPointF &point2,
                                bool multiRegionSelection,
                                bool multiSelectionRegion)
{
  // We are asked to create a region selection encompassing the two
  // points passed as parameter.

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

      return 0;
    }

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "selectRegion with params:"
  // 	      << point1 << point2;

  if(!multiRegionSelection)
    {
      // multiRegionSelection: 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(!multiSelectionRegion)
    {
      // multiSelectionRegion: 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.

      RegionSelection *preSelection =
        regionSelectionEncompassing(point1, point2);

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

          deselectRegion(preSelection);
        }
    }

  // At this point we know we can select the region.

  RegionSelection *selection = new RegionSelection(mp_view);

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

  if(result < 1)
    {
      delete selection;
      return result;
    }

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


  m_regionSelectionList.append(selection);

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

  return result;
}


int
SequenceSelection::selectRegion(int index1,
                                int index2,
                                bool multiRegionSelection,
                                bool multiSelectionRegion)
{
  // We are asked to create a region selection encompassing the two
  // indices passed as parameter.

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "selectRegion with params:"
  // 	      << index1 << index2;

  if(!multiRegionSelection)
    {
      // multiRegionSelection: 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(!multiSelectionRegion)
    {
      // multiSelectionRegion: 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.

      RegionSelection *preSelection =
        regionSelectionEncompassing(index1, index2);

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

          deselectRegion(preSelection);
        }
    }

  // At this point we know we can select the region.

  RegionSelection *selection = new RegionSelection(mp_view);

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

  if(result < 1)
    {
      delete selection;
      return result;
    }

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


  m_regionSelectionList.append(selection);

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

  return result;
}


int
SequenceSelection::deselectRegions()
{
  while(!m_regionSelectionList.isEmpty())
    delete m_regionSelectionList.takeFirst();

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

  return 1;
}


int
SequenceSelection::reselectRegions()
{
  for(int iter = 0; iter < m_regionSelectionList.size(); ++iter)
    {
      RegionSelection *iterRegionSelection = m_regionSelectionList.at(iter);

      iterRegionSelection->redrawMark();
    }

  return m_regionSelectionList.size();
}


int
SequenceSelection::deselectRegion(RegionSelection *regionSelection)
{
  Q_ASSERT(regionSelection);

  for(int iter = 0; iter < m_regionSelectionList.size(); ++iter)
    {
      RegionSelection *iterRegionSelection = m_regionSelectionList.at(iter);

      if(iterRegionSelection == regionSelection)
        {
          m_regionSelectionList.takeAt(iter);

          // 	    qDebug() << __FILE__ << __LINE__
          // 		      << "deselectRegion:" << iterRegionSelection;

          delete(iterRegionSelection);

          return 1;
        }
    }

  return 0;
}


int
SequenceSelection::deselectLastRegion()
{
  if(m_regionSelectionList.isEmpty())
    return 0;

  RegionSelection *selection = m_regionSelectionList.takeLast();

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "deselectLastRegion:" << selection;

  delete(selection);

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

  return 1;
}


int
SequenceSelection::deselectRegionsButLast()
{
  if(m_regionSelectionList.isEmpty())
    return 0;

  while(m_regionSelectionList.size() > 1)
    delete m_regionSelectionList.takeFirst();

  return 1;
}

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 regions in the sequence, BUT each region
  // is selected only once.

  int listIndex = 0;

  for(int iter = 0; iter < m_regionSelectionList.size(); ++iter)
    {
      RegionSelection *iterRegionSelection = m_regionSelectionList.at(iter);

      RegionSelection *foundRegionSelection =
        regionSelectionEncompassing(iterRegionSelection->startIndex(),
                                    iterRegionSelection->endIndex(),
                                    &listIndex);

      while(foundRegionSelection)
        {
          if(iterRegionSelection != foundRegionSelection)
            {
              // Use the removeAll function, because we only have
              // one such pointer anyway, and the removeOne()
              // function is only available with Qt 4.4.0.
              m_regionSelectionList.removeAll(foundRegionSelection);
              delete(foundRegionSelection);

              --listIndex;
            }

          ++listIndex;

          foundRegionSelection =
            regionSelectionEncompassing(iterRegionSelection->startIndex(),
                                        iterRegionSelection->endIndex(),
                                        &listIndex);
        }
    }

  return 1;
}


RegionSelection *
SequenceSelection::regionSelectionEncompassing(int index1,
                                               int index2,
                                               int *listIndex)
{
  // Return the region selection that contains the vignette at index
  // 'index1' and 'index2'

  int iter = 0;

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "regionSelectionEncompassing: searching for indices:"
  // 	      << index1 << "and" << index2;

  if(listIndex)
    {
      if(*listIndex >= 0 && *listIndex < m_regionSelectionList.size())
        iter = *listIndex;
      else if(*listIndex >= m_regionSelectionList.size())
        return 0;
    }

  for(; iter < m_regionSelectionList.size(); ++iter)
    {
      RegionSelection *selection = m_regionSelectionList.at(iter);

      if(selection->encompassing(index1, index2))
        {
          if(listIndex)
            *listIndex = iter;

          // 	    qDebug() << __FILE__ << __LINE__
          // 		      << "regionSelectionEncompassing: found selection:"
          // 		      << selection->startIndex() << selection->endIndex();

          return selection;
        }
    }

  return 0;
}


RegionSelection *
SequenceSelection::regionSelectionEncompassing(const QPointF &point1,
                                               const QPointF &point2,
                                               int *listIndex)
{
  // 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 firstIndex  = mp_view->vignetteIndex(point1);
  int secondIndex = mp_view->vignetteIndex(point2);

  //     qDebug() << __FILE__ << __LINE__
  // 	      << "regionSelectionEncompassing:"
  // 	      << point1 << point2 << "giving indices:"
  // 	      << firstIndex << "--" << secondIndex;

  int temp;

  if(firstIndex > secondIndex)
    {
      temp        = secondIndex;
      secondIndex = firstIndex;
      firstIndex  = temp;
    }

  // 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 secondIndex - 1
  // below.

  //     return regionSelectionEncompassing(firstIndex, secondIndex - 1,
  // 					listIndex);

  return regionSelectionEncompassing(firstIndex, secondIndex, listIndex);
}


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


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

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

  return true;
}


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


bool
SequenceSelection::selectionIndices(CoordinateList *coordList)
{
  int count = 0;

  // The coordList should be empty. If not we empty it right away.

  if(coordList && !coordList->isEmpty())
    coordList->empty();

  for(int iter = 0; iter < m_regionSelectionList.size(); ++iter)
    {
      RegionSelection *regionSelection = m_regionSelectionList.at(iter);

      // Allocate a Coordinates instance to store the start and end
      // indices of the regionSelection.
      Coordinates *coord = regionSelection->coordinates();

      if(coordList)
        coordList->append(coord);

      ++count;
    }

  return count;
}


void
SequenceSelection::debugSelectionPutStdErr()
{
  qDebug() << __FILE__ << __LINE__ << "SequenceSelection:";

  for(int iter = 0; iter < m_regionSelectionList.size(); ++iter)
    {
      RegionSelection *iterRegionSelection = m_regionSelectionList.at(iter);
      iterRegionSelection->debugSelectionIndicesPutStdErr();
    }
}


} // namespace msXpSmassXpert
