/* Copyright (C) 2022-2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Sergey Gorbunov, Sergei Zharko [committer] */

/// \file   CaDataManager.h
/// \brief  Input-output data manager for L1 tracking algorithm
/// \since  08.08.2022
/// \author S.Zharko <s.zharko@gsi.de>

#include "CaDataManager.h"

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

#include <fstream>

using cbm::algo::ca::DataManager;
using cbm::algo::ca::InputData;

// ---------------------------------------------------------------------------------------------------------------------
//
bool DataManager::SendInputData(InputData& destination)
{
  // Set boundary hit indexes
  InitData();

  // Check data before input
  if (CheckInputData<constants::control::InputDataQaLevel>()) {
    destination = std::move(fInputData);
    assert(this->GetNofHits() == 0);
    return true;
  }
  LOG(error) << "L1: Attempt to set up inconsistent input data";
  return false;
}

// ---------------------------------------------------------------------------------------------------------------------
//
InputData&& DataManager::TakeInputData()
{
  // Init the input data
  InitData();

  // Check the input data
  // TODO: Provide assertion
  // if (CheckInputData<constants::control::InputDataQaLevel>()) {
  //   pAlgo->ReceiveInputData(std::move(fInputData));
  //
  // }

  return std::move(fInputData);
}

// ---------------------------------------------------------------------------------------------------------------------
//
void DataManager::ReadInputData(const std::string& fileName)
{
  // Reset input data object
  ResetInputData();
  LOG(info) << "L1: Input data will be read from file \"" << fileName << "\"";

  // Open input binary file
  std::ifstream ifs(fileName, std::ios::binary);
  if (!ifs) { LOG(fatal) << "L1: input data reader: data file \"" << fileName << "\" was not found"; }

  // Get InputData object
  try {
    boost::archive::binary_iarchive ia(ifs);
    ia >> fInputData;
  }
  catch (const std::exception&) {
    LOG(fatal) << "L1: input data reader: data file \"" << fileName << "\" has incorrect data format or was corrupted";
  }
}

// ---------------------------------------------------------------------------------------------------------------------
//
void DataManager::ResetInputData(ca::HitIndex_t nHits) noexcept
{
  InputData tmp;
  fInputData.Swap(tmp);
  fLastStreamId = -1;
  fInputData.fStreamStartIndices.reserve(2000);  // TODO: What are these numbers? Please, put them into constants.h
  fInputData.fStreamStopIndices.reserve(2000);
  fInputData.fHits.reserve(nHits);
}

// ---------------------------------------------------------------------------------------------------------------------
//
void DataManager::InitData()
{
  // set the end pointers to data streams

  //std::cout << "N data streams: " << fInputData.fStreamStartIndices.size() << std::endl;

  // TODO: SZh 14.08.2023: Move the max allowed number of streams to the constants.h
  if (fInputData.fStreamStartIndices.size() > 3000) {
    LOG(warning) << "L1: unexpected order of input data: too many data streams!!! ";
    fInputData.fStreamStartIndices.reduce(3000);
  }
  int nStreams = fInputData.fStreamStartIndices.size();
  fInputData.fStreamStopIndices.reset(nStreams);
  for (int i = 0; i < nStreams - 1; i++) {
    fInputData.fStreamStopIndices[i] = fInputData.fStreamStartIndices[i + 1];
  }
  if (nStreams > 0) { fInputData.fStreamStopIndices[nStreams - 1] = fInputData.fHits.size(); }
}

// ---------------------------------------------------------------------------------------------------------------------
//
void DataManager::WriteInputData(const std::string& fileName) const
{
  // Check current data object for consistency
  if (!CheckInputData<constants::control::InputDataQaLevel>()) {
    LOG(error) << "L1: input data writer: attempt to write invalid input data object to file \"" << fileName << "\"";
    return;
  }

  // Open output binary file
  std::ofstream ofs(fileName, std::ios::binary);
  if (!ofs) {
    LOG(error) << "L1: input data writer: failed opening file \"" << fileName << " for writing input data\"";
    return;
  }

  // Serialize InputData object and write
  boost::archive::binary_oarchive oa(ofs);
  oa << fInputData;
}