/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
#pragma once
#ifndef WRITER_HPP
#define WRITER_HPP

#include <cstdint>
#include <filesystem>
#include <fstream>
#include <future>
#include <iostream>
#include <string>

#include <ff/ff.hpp>

#include "task.hpp"

using namespace ff;

template <typename T> class Writer : public ff::ff_node_t<Task<T>, void> {
  public:
    void *svc(Task<T> *);
    void operator()(std::vector<std::shared_future<Task<T> *>> *);
};

// svc function for fastflow library
template <typename T> void *Writer<T>::svc(Task<T> *in) {
    write_one_image(in);
    return this->GO_ON;
}

// operator for std thread
template <typename T>
void Writer<T>::operator()(
    std::vector<std::shared_future<Task<T> *>> *ResultsVector) {
    if (!ResultsVector) {
        std::cerr << "Error: invalid pointer to vector of futures "
                     "[Writer::operator()]"
                  << std::endl;
        return;
    }
    for (auto Result : *ResultsVector) {
        Task<T> *t = Result.get();
        write_one_image(t);
        delete (t);
    }
}

// writes out just one image, prepend the output image with "out_"
template <typename T> void write_one_image(Task<T> *t) {
    std::string Pre = "out_";
    std::filesystem::path Path = t->PathName;
    std::string NameNew = Pre + Path.filename().string();
    Path.replace_filename(NameNew);

    std::fstream File{Path, File.out | File.binary};
    if (!File.is_open()) {
        std::cerr << "Error: Failed to open " << t->PathName.string() << std::endl;
        return;
    } else {
        File.write(reinterpret_cast<char *>(&t->Rows), sizeof(t->Rows));
        File.write(reinterpret_cast<char *>(&t->Cols), sizeof(t->Cols));

        // use temporary buffer to not call the write function too frequently
        T *buffer = new T[t->Cols];
        for (int x = 0; x < t->Rows; ++x) {
            for (int y = 0; y < t->Cols; ++y) {
                buffer[y] = (*t->VectorData)[x][y];
            }
            File.write(reinterpret_cast<char *>(buffer), t->Cols * sizeof(T));
        }
        delete[] buffer;
        File.close();
    }
}

#endif /* WRITER_HPP */
