text: #include "PbbamInternalConfig.h"
  
  #include <pbbam/virtual/ZmwReadStitcher.h>
  
  #include <deque>
  #include <stdexcept>
  #include <utility>
  
  #include <pbbam/DataSet.h>
  #include <pbbam/EntireFileQuery.h>
  #include <pbbam/PbiFilter.h>
  #include <pbbam/PbiFilterQuery.h>
  
  #include "VirtualStitching.h"
  #include "VirtualZmwReader.h"
  
  namespace PacBio {
  namespace BAM {
  
  class ZmwReadStitcher::ZmwReadStitcherPrivate
  {
  public:
      ZmwReadStitcherPrivate(std::string primaryBamFilePath, std::string scrapsBamFilePath,
                             PbiFilter filter)
          : filter_{std::move(filter)}
      {
          sources_.push_back({std::move(primaryBamFilePath), std::move(scrapsBamFilePath)});
          OpenNextReader();
      }
  
      ZmwReadStitcherPrivate(const DataSet& dataset) : filter_{PbiFilter::FromDataSet(dataset)}
      {
          sources_ = SourcesFromDataset(dataset);
          OpenNextReader();
      }
  
      bool HasNext() const { return (currentReader_ && currentReader_->HasNext()); }
  
      VirtualZmwBamRecord Next()
      {
          if (currentReader_) {
              const auto result = currentReader_->Next();
              if (!currentReader_->HasNext()) OpenNextReader();
              return result;
          }
  
          // no reader active
          throw std::runtime_error{
              "[pbbam] ZMW stitching ERROR: "
              "no readers active, make sure you use "
              "ZmwReadStitcher::HasNext before "
              "requesting next record"};
      }
  
      std::vector<BamRecord> NextRaw()
      {
          if (currentReader_) {
              const auto result = currentReader_->NextRaw();
              if (!currentReader_->HasNext()) OpenNextReader();
              return result;
          }
  
          // no reader active
          throw std::runtime_error{
              "[pbbam] ZMW stitching ERROR: "
              "no readers active, make sure you use "
              "ZmwReadStitcher::HasNext before "
              "requesting next group of records"};
      }
  
      BamHeader PrimaryHeader() const { return currentReader_->PrimaryHeader(); }
  
      BamHeader ScrapsHeader() const { return currentReader_->ScrapsHeader(); }
  
      BamHeader StitchedHeader() const { return currentReader_->StitchedHeader(); }
  
  private:
      StitchingSources sources_;
      std::unique_ptr<VirtualZmwReader> currentReader_;
      PbiFilter filter_;
  
      void OpenNextReader()
      {
          currentReader_.reset(nullptr);
  
          // find next source pair with data
          while (!sources_.empty()) {
              const auto nextSource = sources_.front();
              sources_.pop_front();
  
              currentReader_ =
                  std::make_unique<VirtualZmwReader>(nextSource.first, nextSource.second, filter_);
              if (currentReader_->HasNext()) return;
          }
      }
  };
  
  // --------------------------------
  // ZmwReadStitcher implementation
  // --------------------------------
  
  ZmwReadStitcher::ZmwReadStitcher(std::string primaryBamFilePath, std::string scrapsBamFilePath)
      : ZmwReadStitcher{std::move(primaryBamFilePath), std::move(scrapsBamFilePath), PbiFilter{}}
  {
  }
  
  ZmwReadStitcher::ZmwReadStitcher(std::string primaryBamFilePath, std::string scrapsBamFilePath,
                                   PbiFilter filter)
      : d_{std::make_unique<ZmwReadStitcherPrivate>(std::move(primaryBamFilePath),
                                                    std::move(scrapsBamFilePath), std::move(filter))}
  {
  }
  
  ZmwReadStitcher::ZmwReadStitcher(const DataSet& dataset)
      : d_{std::make_unique<ZmwReadStitcherPrivate>(dataset)}
  {
  }
  
  ZmwReadStitcher::~ZmwReadStitcher() = default;
  
  bool ZmwReadStitcher::HasNext() { return d_->HasNext(); }
  
  VirtualZmwBamRecord ZmwReadStitcher::Next() { return d_->Next(); }
  
  std::vector<BamRecord> ZmwReadStitcher::NextRaw() { return d_->NextRaw(); }
  
  BamHeader ZmwReadStitcher::PrimaryHeader() const { return d_->PrimaryHeader().DeepCopy(); }
  
  BamHeader ZmwReadStitcher::ScrapsHeader() const { return d_->ScrapsHeader().DeepCopy(); }
  
  BamHeader ZmwReadStitcher::StitchedHeader() const { return d_->StitchedHeader().DeepCopy(); }
  
  }  // namespace BAM
  }  // namespace PacBio
  