/**
 * \file pappsomspp/xicextractor/private/msrunxicextractordiskbuffer.cpp
 * \date 18/05/2018
 * \author Olivier Langella
 * \brief proteowizard based XIC extractor featuring disk cache + write buffer
 */

/*******************************************************************************
 * Copyright (c) 2018 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/>.
 *
 * Contributors:
 *     Olivier Langella <Olivier.Langella@u-psud.fr> - initial API and
 *implementation
 ******************************************************************************/


#include "msrunxicextractordiskbuffer.h"
#include <QDebug>
#include "pappsomspp/core/pappsoexception.h"
#include "pappsomspp/core/massspectrum/massspectrum.h"

namespace pappso
{

MsRunXicExtractorDiskBuffer::MsRunXicExtractorDiskBuffer(MsRunReaderSPtr &msrun_reader,
                                                         const QDir &temporary_dir)
  : MsRunXicExtractorDisk(msrun_reader, temporary_dir)
{

  m_sliceBufferMap.clear();
}

MsRunXicExtractorDiskBuffer::MsRunXicExtractorDiskBuffer(const MsRunXicExtractorDiskBuffer &other)
  : MsRunXicExtractorDisk(other)
{

  m_sliceBufferMap.clear();
}

MsRunXicExtractorDiskBuffer::~MsRunXicExtractorDiskBuffer()
{
}

void
MsRunXicExtractorDiskBuffer::storeSlices(std::map<unsigned int, MassSpectrum> &slice_vector,
                                         std::size_t ipos)
{

  m_bufferSize++;
  for(auto &&msrun_slice : slice_vector)
    {
      appendSliceInBuffer(msrun_slice.first, msrun_slice.second, ipos);
    }

  if(m_bufferSize == m_bufferMaxSize)
    {
      flushBufferOnDisk();
    }
}

void
MsRunXicExtractorDiskBuffer::appendSliceInBuffer(unsigned int slice_number,
                                                 MassSpectrum &spectrum,
                                                 std::size_t ipos)
{
  // qDebug();

  std::size_t spectrum_size = spectrum.size();

  if(spectrum_size == 0)
    return;
  try
    {
      std::pair<std::map<unsigned int, QByteArray>::iterator, bool> ret =
        m_sliceBufferMap.insert(std::pair<unsigned int, QByteArray>(slice_number, QByteArray()));


      if(ret.second)
        { // new buffer
          ret.first->second.resize(0);
          QDataStream outstream(&ret.first->second, QIODevice::WriteOnly);
          outstream << (quint32)ipos;
          outstream << spectrum;
        }
      else
        {
          QDataStream outstream(&ret.first->second, QIODevice::WriteOnly | QIODevice::Append);
          outstream << (quint32)ipos;
          outstream << spectrum;
        }
    }
  catch(PappsoException &error_pappso)
    {
      throw pappso::PappsoException(QObject::tr("appendSliceInBuffer : error ipos=%1 :\n%2")
                                      .arg(ipos)
                                      .arg(error_pappso.qwhat()));
    }
  catch(std::exception &error)
    {
      throw pappso::PappsoException(
        QObject::tr("appendSliceInBuffer slice_number=%1 ipos=%2 error :\n%3")
          .arg(slice_number)
          .arg(ipos)
          .arg(error.what()));
    }
  // qDebug();
}


void
MsRunXicExtractorDiskBuffer::flushBufferOnDisk()
{
  qDebug();

  try
    {
      for(auto &buffer_pair : m_sliceBufferMap)
        {

          if(buffer_pair.second.size() > 0)
            {
              QFile slice_file(
                QString("%1/%2").arg(mpa_temporaryDirectory->path()).arg(buffer_pair.first));
              bool new_file = false;
              if(!slice_file.exists())
                {
                  new_file = true;

                  if(!slice_file.open(QIODevice::WriteOnly))
                    {
                      throw pappso::PappsoException(
                        QObject::tr("unable to open file %1").arg(slice_file.fileName()));
                    }
                }
              else
                {
                  if(!slice_file.open(QIODevice::WriteOnly | QIODevice::Append))
                    {
                      throw pappso::PappsoException(
                        QObject::tr("unable to open file %1").arg(slice_file.fileName()));
                    }
                }

              QDataStream stream(&slice_file);

              if(new_file)
                {
                  stream << (quint32)buffer_pair.first;
                  stream << (quint32)m_rtSize;
                  stream.writeRawData(buffer_pair.second.constData(), buffer_pair.second.size());
                }
              else
                {
                  stream.writeRawData(buffer_pair.second.constData(), buffer_pair.second.size());
                }


              slice_file.flush();
              slice_file.close();
            }
          // buffer_pair.second = std::vector<MassSpectrum>();
        }

      m_bufferSize = 0;
      m_sliceBufferMap.clear();
    }
  catch(PappsoException &error_pappso)
    {
      throw pappso::PappsoException(
        QObject::tr("flushBufferOnDisk error :\n%1").arg(error_pappso.qwhat()));
    }
  catch(std::exception &error)
    {
      throw pappso::PappsoException(QObject::tr("flushBufferOnDisk error :\n%1").arg(error.what()));
    }
  qDebug();
}


void
MsRunXicExtractorDiskBuffer::endPwizRead()
{
  flushBufferOnDisk();

  m_sliceBufferMap.clear();

  msp_msrun_reader.get()->releaseDevice();
}
} // namespace pappso
