/**
 * \file pappsomspp/fasta/fastafileindexer.cpp
 * \date 22/06/2109
 * \author Olivier Langella
 * \brief Quick random access to sequences in a fasta file using an index
 */

/*******************************************************************************
 * Copyright (c) 2019 Olivier Langella <Olivier.Langella@u-psud.fr>.
 *
 * This file is part of the PAPPSOms++ library.
 *
 *     PAPPSOms++ 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.
 *
 *     PAPPSOms++ 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 PAPPSOms++.  If not, see <http://www.gnu.org/licenses/>.
 *
 ******************************************************************************/

#include "fastafileindexer.h"

#include <QDebug>
#include <QTextStream>
#include <QDataStream>
#include <QFileInfo>
#include "pappsomspp/core/exception/exceptionoutofrange.h"
#include "fastareader.h"

namespace pappso
{
FastaFileIndexer::FastaFileIndexer(const QFileInfo &fastaFile)
  : m_fasta_file(fastaFile.absoluteFilePath())
{

  if(m_fasta_file.fileName().isEmpty())
    {
      throw PappsoException(QObject::tr("No FASTA file name specified"));
    }
  if(m_fasta_file.open(QIODevice::ReadOnly))
    {
      parseFastaFile();
      m_fasta_file.close();
    }
  else
    {
      throw PappsoException(
        QObject::tr("ERROR opening FASTA file %1 for read").arg(fastaFile.fileName()));
    }
}

FastaFileIndexer::FastaFileIndexer(const FastaFileIndexer &other)
  : m_fasta_file(other.m_fasta_file.fileName())
{

  m_indexArray      = other.m_indexArray;
  mpa_sequenceTxtIn = nullptr;
}
FastaFileIndexer::~FastaFileIndexer()
{
  close();
}


void
FastaFileIndexer::parseFastaFile()
{

  qDebug();
  QDataStream bin_in(&m_fasta_file);
  qint64 position = 0;

  // QChar first_char;
  // txt_in >> first_char;
  qint8 char_in;
  bin_in >> char_in;
  while(!bin_in.atEnd() && (char_in < (qint8)21))
    { // eat Windows \r\n
      position++;
      bin_in >> char_in;
    }
  while(!bin_in.atEnd())
    {
      // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
      //         << " first_char=" << first_char;
      if(char_in == (qint8)'>')
        {

          // qDebug() << __FILE__ << " " << __FUNCTION__ << " " << __LINE__
          //        << " index=" << m_indexArray.size()
          //         << " position=" << position;
          m_indexArray.push_back(position);
        }
      // eat line
      position++;
      bin_in >> char_in;
      while(!bin_in.atEnd() && (char_in > (qint8)20))
        {
          position++;
          bin_in >> char_in;
        }
      position++;
      bin_in >> char_in;

      if(!bin_in.atEnd() && (char_in < (qint8)21))
        { // eat Windows \r\n
          position++;
          bin_in >> char_in;
        }
    }
  qDebug();
}

void
FastaFileIndexer::open()
{
  if(mpa_sequenceTxtIn != nullptr)
    return;
  if(m_fasta_file.open(QIODevice::ReadOnly))
    {
      mpa_sequenceTxtIn = new QTextStream(&m_fasta_file);
    }
  else
    {
      throw PappsoException(
        QObject::tr("ERROR opening FASTA file %1 for read").arg(m_fasta_file.fileName()));
    }
}

void
FastaFileIndexer::close()
{
  if(mpa_sequenceTxtIn != nullptr)
    {
      delete mpa_sequenceTxtIn;
      mpa_sequenceTxtIn = nullptr;
      m_fasta_file.close();
    }
}

void
FastaFileIndexer::getSequenceByIndex(FastaHandlerInterface &fasta_handler, std::size_t index)
{
  open();

  qDebug() << " goto=" << index << " pos=" << m_indexArray[index];
  bool seek_ok;
  if((index < m_indexArray.size()) && (seek_ok = mpa_sequenceTxtIn->seek(m_indexArray[index])))
    {

      qDebug() << " realpos=" << mpa_sequenceTxtIn->pos();
      ;
      if(!seek_ok)
        {

          throw PappsoException(QObject::tr("ERROR FastaFileIndexer : seek to "
                                            "sequence %1, position %2 failed")
                                  .arg(index)
                                  .arg(m_indexArray[index]));
        }
      FastaReader reader(fasta_handler);
      reader.parseOnlyOne(*mpa_sequenceTxtIn);
    }
  else
    {
      throw ExceptionOutOfRange(QObject::tr("ERROR reading FASTA file %1 : sequence index %2 "
                                            "unreachable, array size=%3")
                                  .arg(m_fasta_file.fileName())
                                  .arg(index)
                                  .arg(m_indexArray.size()));
    }
}


FastaFileIndexerSPtr
FastaFileIndexer::makeFastaFileIndexerSPtr() const
{

  return std::make_shared<FastaFileIndexer>(*this);
}


std::size_t
FastaFileIndexer::size() const
{
  return m_indexArray.size();
}
} // namespace pappso
