/* 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 "SequenceSelection.hpp"
#include "SequenceEditorGraphicsView.hpp"


namespace MsXpS
{

namespace massxpert
{


  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(libXpertMass::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 libXpertMass::Coordinates instance to store the start and end
        // indices of the regionSelection.
        libXpertMass::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 massxpert

} // namespace MsXpS
