// -*- C++ -*-
//
// This file is part of YODA -- Yet more Objects for Data Analysis
// Copyright (C) 2008-2022 The YODA collaboration (see AUTHORS for details)
//
#include "YODA/WriterH5.h"
#include "YODA/Utils/H5Utils.h"

#include <string>

using namespace std;

namespace YODA {


  /// Singleton creation function
  Writer& WriterH5::create() {
    static WriterH5 _instance;
    _instance.setPrecision(6); //< not used
    return _instance;
  }


  /// YODA text-format version
  ///
  /// - V1 = H5 format for YODA2 types
  static const size_t YODA_H5_FORMAT_VERSION = 1;


  void WriterH5::writeAOS(YODA_H5::File& h5, const std::vector<const AnalysisObject*>& aos) {

    // Work out the length of the concatenated serialized streams
    size_t annolen = 0, datalen = 0;
    vector<size_t> annosizes, datasizes, labelsizes;
    annosizes.reserve(aos.size());
    labelsizes.reserve(aos.size());
    datasizes.reserve(aos.size()+1); //< +1 for # of AOs requiring error labels
    vector<string> labels; // list of error labels
    for (const AnalysisObject* ao : aos) {
      annosizes.emplace_back(ao->lengthMeta() - 1); //< skip "Title" key
      datasizes.emplace_back(ao->lengthContent());
      annolen += annosizes.back();
      datalen += datasizes.back();
      // if the AO is an Estimate, extract the error labels
      ao->_extractLabels(labels, labelsizes);
    }
    datasizes.emplace_back(labelsizes.size());
    //size_t annochunk = annolen/aos.size();
    //size_t datachunk = datalen/aos.size();
    size_t annochunk = std::min(annolen, (size_t)1200);
    size_t datachunk = std::min(datalen, (size_t)1500);

    // create empty H5 DataSets
    H5DataSetWriter<string> annoset(h5, "annotations", annolen, annochunk, _compress);
    H5DataSetWriter<double> dataset(h5, "content", datalen, datachunk, _compress);

    // Now that we know the sizes of all AOs,
    // fill an uber-vector of serialized sub-vectors
    // as well as a map of edge handlers for binned AOs
    map<string, EdgeHandlerBasePtr> layout;
    layout["sizeinfo"] = std::make_shared<EdgeHandler<size_t>>();
    static_pointer_cast<EdgeHandler<size_t>>(layout["sizeinfo"])->extend(std::move(annosizes));
    static_pointer_cast<EdgeHandler<size_t>>(layout["sizeinfo"])->extend(std::move(datasizes));
    static_pointer_cast<EdgeHandler<size_t>>(layout["sizeinfo"])->extend(std::move(labelsizes));
    vector<string> aoinfo; aoinfo.reserve(H5FileManager::AO_META*aos.size());
    for (const AnalysisObject* ao : aos) {

      // save metadata about this AO
      aoinfo.emplace_back(ao->path());
      aoinfo.emplace_back(Utils::toUpper(ao->type()));

      // retrieve annotations for this AO
      vector<string> annos = ao->serializeMeta(); //< skips Path and Title
      annos.emplace_back(ao->title()); // title value should be at the end

      // retrieve content for this AO
      vector<double> content = ao->serializeContent();

      // write to file
      annoset.writeSlice(std::move(annos));
      dataset.writeSlice(std::move(content));

      // if the AO is a Fillable, extract its binning
      ao->_extractEdges(layout, labels);
    }

    // write AO metadata
    auto meta = H5DataSet(h5, "aoinfo", std::move(aoinfo), _compress);

    // write layout and attribute for file-level metadata
    meta.createAttribute("meta", vector<size_t>{YODA_H5_FORMAT_VERSION, aos.size()});

    // write error source labels
    (void)H5DataSet(h5, "labels", std::move(labels), _compress);

    // write bin edges
    for (const auto& item : layout) {
      item.second->writeToFile(item.first, h5, _compress);
    }

  }

}
