#ifndef FILTERBANKSET_H
#define FILTERBANKSET_H

#include <deque>
#include <memory>
#include <queue>
#include <string>
#include <vector>

#include "imageset.h"

#include "../util/logger.h"

namespace imagesets {

/**
 * Reader for the Sigproc filter bank format.
 * See https://sigproc.sourceforge.net/sigproc.pdf , chapter 3.
 */
class FilterBankSet final : public ImageSet {
 public:
  explicit FilterBankSet(const std::string& location);

  FilterBankSet(const FilterBankSet& source);

  ~FilterBankSet() noexcept = default;

  std::unique_ptr<ImageSet> Clone() override {
    std::unique_ptr<FilterBankSet> set(new FilterBankSet(*this));
    return set;
  }

  size_t Size() const override { return interval_count_; }

  std::string Name() const override { return location_; }

  std::string Description(const ImageSetIndex& index) const override;

  std::vector<std::string> Files() const override {
    return std::vector<std::string>{location_};
  }

  std::string TelescopeName() override;

  void AddReadRequest(const ImageSetIndex& index) override;

  void PerformReadRequests(class ProgressListener& progress) override;

  std::unique_ptr<BaselineData> GetNextRequested() override;

  void AddWriteFlagsTask(const ImageSetIndex& index,
                         std::vector<Mask2DCPtr>& flags) override;

  void Initialize() override;

  void PerformWriteDataTask(const ImageSetIndex& index,
                            std::vector<Image2DCPtr> realImages,
                            std::vector<Image2DCPtr> imaginaryImages) override;

  void PerformWriteFlagsTask() override {}

  bool HasCrossCorrelations() const override { return false; }

  double CentreFrequency() const {
    return (bank1_centre_frequency +
            (bank_channel_bandwidth_ * channel_count_ * 0.5)) *
           1e6;
  }
  double ChannelWidth() const {
    return std::fabs(bank_channel_bandwidth_) * 1e6;
  }
  double TimeResolution() const { return time_of_sample_; }

 private:
  friend class FilterBankSetIndex;
  std::string location_;

  double time_of_sample_;
  double start_time_;
  double bank1_centre_frequency;
  double bank_channel_bandwidth_;
  size_t channel_count_;
  size_t if_count_;
  size_t bit_count_;
  size_t sample_count_;
  size_t n_beams_;
  size_t i_beam_;
  int machine_id_;
  int telescope_id_;
  size_t interval_count_;
  std::streampos header_end_;

  std::deque<BaselineData*> requests_;
  std::queue<std::unique_ptr<BaselineData>> baselines_;

  static int32_t ReadInt(std::istream& str) {
    int32_t val;
    str.read(reinterpret_cast<char*>(&val), sizeof(int32_t));
    return val;
  }

  static double ReadDouble(std::istream& str) {
    double val;
    str.read(reinterpret_cast<char*>(&val), sizeof(double));
    return val;
  }

  static std::string ReadString(std::istream& str) {
    const int32_t length = ReadInt(str);
    if (length <= 0 || length >= 80) return std::string();
    std::string data(length, 0);
    str.read(&data[0], length);
    return std::string(&data[0]);
  }
};

}  // namespace imagesets

#endif
