/* 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
#include <QDebug>
#include <QRegExp>
#include <QStringList>


/////////////////////// Local includes
#include <massxpert/nongui/Coordinates.hpp>


namespace msXpSmassXpert
{

///////////////////////// Coordinates /////////////////////////
///////////////////////// Coordinates /////////////////////////
///////////////////////// Coordinates /////////////////////////
///////////////////////// Coordinates /////////////////////////
///////////////////////// Coordinates /////////////////////////


Coordinates::Coordinates(int start, int end)
{
  if(start > end)
    {
      m_start = end;
      m_end   = start;
    }
  else
    {
      m_start = start;
      m_end   = end;
    }
}


Coordinates::Coordinates(const Coordinates &other)
  : m_start(other.m_start), m_end(other.m_end)
{
  int temp;

  if(m_start > m_end)
    {
      temp    = m_end;
      m_end   = m_start;
      m_start = temp;
    }
}


Coordinates::~Coordinates()
{
}


void
Coordinates::setStart(int value)
{
  if(value > m_end)
    {
      m_start = m_end;
      m_end   = value;
    }
  else
    {
      m_start = value;
    }
}


int
Coordinates::start() const
{
  return m_start;
}


void
Coordinates::incrementEnd()
{
  ++m_end;
}


void
Coordinates::setEnd(int value)
{
  m_end = value;
}


int
Coordinates::end() const
{
  return m_end;
}


int
Coordinates::length() const
{
  return (m_end - m_start + 1);
}


QString
Coordinates::indicesAsText() const
{
  QString text = QString("[%1--%2]").arg(m_start).arg(m_end);

  return text;
}


QString
Coordinates::positionsAsText() const
{
  QString text = QString("[%1--%2]").arg(m_start + 1).arg(m_end + 1);

  return text;
}


void
Coordinates::reset()
{
  m_start = 0;
  m_end   = 0;
}


///////////////////////// CoordinateList /////////////////////////
///////////////////////// CoordinateList /////////////////////////
///////////////////////// CoordinateList /////////////////////////
///////////////////////// CoordinateList /////////////////////////
///////////////////////// CoordinateList /////////////////////////


CoordinateList::CoordinateList(QString comment, QList<Coordinates *> *list)
  : m_comment(comment)
{
  if(list)
    {
      for(int iter = 0; iter < list->size(); ++iter)
        {
          Coordinates *iterCoordinates = list->at(iter);

          Coordinates *coordinates = new Coordinates(*iterCoordinates);

          append(coordinates);
        }
    }
}


CoordinateList::CoordinateList(const CoordinateList &other)
  : m_comment(other.m_comment)
{
  for(int iter = 0; iter < other.size(); ++iter)
    {
      Coordinates *iterCoordinates = other.at(iter);

      Coordinates *coordinates = new Coordinates(*iterCoordinates);

      append(coordinates);
    }
}


CoordinateList::~CoordinateList()
{
  // The members of the list were allocated with new()...
  qDeleteAll(begin(), end());
  clear();
}


CoordinateList &
CoordinateList::operator=(const CoordinateList &other)
{
  if(&other == this)
    return *this;

  m_comment = other.m_comment;

  empty();

  for(int iter = 0; iter < other.size(); ++iter)
    {
      Coordinates *iterCoordinates = other.at(iter);

      Coordinates *coordinates = new Coordinates(*iterCoordinates);

      append(coordinates);
    }

  return *this;
}


void
CoordinateList::setCoordinates(const Coordinates &coordinates)
{
  empty();

  Coordinates *newCoordinates = new Coordinates(coordinates);
  append(newCoordinates);
}


void
CoordinateList::setCoordinates(const CoordinateList &list)
{
  empty();

  for(int iter = 0; iter < list.size(); ++iter)
    {
      Coordinates *coordinates = new Coordinates(*list.at(iter));
      append(coordinates);
    }
}


void
CoordinateList::appendCoordinates(const Coordinates &coordinates)
{
  Coordinates *newCoordinates = new Coordinates(coordinates);

  append(newCoordinates);
}


int
CoordinateList::setCoordinates(const QString &string)
{
  // We get a string in the form [xxx-yyy] (there can be more than
  // one such element, if cross-linked oligomers are calculated.
  // "[228-246][276-282][247-275]". Because that string comes from
  // outside of massXpert, it is expected that it contains positions
  // and not indexes. So we have to decrement the start and end
  // values by one.

  // qDebug() <<__FILE__ << __LINE__
  //          << "string:" << string;

  // Whatever will happen, we wanto set coordinates, not append,
  // thus first empty().
  empty();

  if(!string.contains('[') || !string.contains(']') || !string.contains('-'))
    return -1;

  QStringList coordinateList = string.split(']', QString::SkipEmptyParts);

  // qDebug() << __FILE__ << __LINE__
  //          << "coordinateList:" << coordinateList;

  for(int iter = 0; iter < coordinateList.size(); ++iter)
    {
      QString coordinates = coordinateList.at(iter);

      coordinates = coordinates.remove('[');

      QStringList positionList = coordinates.split('-');

      // qDebug() << __FILE__ << __LINE__
      //          << "positionList:" << positionList;

      if(positionList.size() != 2)
        {
          // Error.
          // qDebug() << __FILE__<< __LINE__
          //          << "error: return -1";

          return -1;
        }

      // At this point we should have two numeric values in the the
      // positionList.
      bool ok   = false;
      int start = positionList.at(0).toInt(&ok);

      // The index might well be 0, but the, ok should be true.

      if(!--start && !ok)
        return -1;

      ok      = false;
      int end = positionList.at(1).toInt(&ok);

      if(!--end && !ok)
        return -1;

      Coordinates *newCoordinates = 0;

      if(start > end)
        newCoordinates = new Coordinates(end, start);
      else
        newCoordinates = new Coordinates(start, end);

      append(newCoordinates);
    }

  QString text = positionsAsText();

  // qDebug() << __FILE__ << __LINE__
  //          << "positionsAsText: " << text;

  return size();
}

void
CoordinateList::setComment(QString text)
{
  m_comment = text;
}


QString
CoordinateList::comment() const
{
  return m_comment;
}


int
CoordinateList::leftMostCoordinates(QList<int> &indexList) const
{
  if(isEmpty())
    return 0;

  while(!indexList.isEmpty())
    indexList.removeFirst();

  int leftMostValue = first()->start();

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      int start = coordinates->start();

      if(start < leftMostValue)
        leftMostValue = start;
    }

  // At this point we now what's the leftmost index. We can use that
  // index to now search for all the items that are also leftmost.

  for(int iter = 0; iter < size(); ++iter)
    {
      if(at(iter)->start() == leftMostValue)
        indexList.append(iter);
    }

  return indexList.size();
}


bool
CoordinateList::isLeftMostCoordinates(Coordinates *coordinates) const
{
  Q_ASSERT(coordinates);

  // Are the coordinates the leftmost coordinates of *this
  // CoordinateList ?

  int value = coordinates->start();

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      if(value > coordinates->start())
        return false;
    }

  return true;
}


int
CoordinateList::rightMostCoordinates(QList<int> &indexList) const
{
  if(isEmpty())
    return 0;

  while(!indexList.isEmpty())
    indexList.removeFirst();

  int rightMostValue = first()->end();

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      int end = coordinates->end();

      if(end > rightMostValue)
        rightMostValue = end;
    }

  // At this point we now what's the rightmost index. We can use
  // that index to now search for all the items that are also
  // rightmost.

  for(int iter = 0; iter < size(); ++iter)
    {
      if(at(iter)->end() == rightMostValue)
        indexList.append(iter);
    }

  return indexList.size();
}


bool
CoordinateList::isRightMostCoordinates(Coordinates *coordinates) const
{
  Q_ASSERT(coordinates);

  // Are the coordinates the rightmost coordinates of *this
  // CoordinateList ?

  int value = coordinates->end();

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      if(value < coordinates->end())
        return false;
    }

  return true;
}


bool
CoordinateList::encompassIndex(int index) const
{
  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      if(index >= coordinates->start() && index <= coordinates->end())
        return true;
    }

  return false;
}


bool
CoordinateList::overlap() const
{
  // Return true if there are overlapping regions in this
  // coordinate list.

  if(size() <= 1)
    return false;

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates coords1 = *at(iter);

      int start1 = coords1.start();
      int end1   = coords1.end();

      for(int jter = 0; jter < size(); ++jter)
        {
          // Do not compare one item to itself.
          if(jter == iter)
            continue;

          Coordinates coords2 = *at(jter);

          int start2 = coords2.start();

          if(start2 <= end1 && start2 >= start1)
            return true;
        }
    }

  return false;
}


QString
CoordinateList::indicesAsText() const
{
  QString text;

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      text +=
        QString("[%1-%2]").arg(coordinates->start()).arg(coordinates->end());
    }

  return text;
}


QString
CoordinateList::positionsAsText() const
{
  QString text;

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      text += QString("[%1-%2]")
                .arg(coordinates->start() + 1)
                .arg(coordinates->end() + 1);
    }

  return text;
}


void
CoordinateList::empty()
{
  qDeleteAll(begin(), end());
  clear();
}


void
CoordinateList::debugPutStdErr()
{
  qDebug() << __FILE__ << __LINE__ << "CoordinateList:";

  QString text;

  for(int iter = 0; iter < size(); ++iter)
    {
      Coordinates *coordinates = at(iter);

      text += coordinates->indicesAsText();
    }

  qDebug() << __FILE__ << __LINE__ << text;
}

} // namespace msXpSmassXpert
