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

#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <future>
#include <iostream>
#include <vector>

#include <ff/ff.hpp>

#include "task.hpp"

using namespace ff;

template <typename T> class Reader : public ff_node_t<Task<T>> {
    std::vector<std::string> Images;

  public:
    Reader(std::vector<std::string> images);
    Task<T> *svc(Task<T> *);
    void operator()(std::vector<std::string> images,
                    std::vector<std::promise<Task<T> *> *> *OutputVector);
};

template <typename T> Task<T> *read_one_image(std::string imagename);

template <typename T>
Reader<T>::Reader(std::vector<std::string> images) : Images(images) {}

// svc function for fastflow library, function as an emitter: generates all
// tasks from the list of paths
template <typename T> Task<T> *Reader<T>::svc(Task<T> *) {
    for (std::string s : Images) {
        Task<T> *t = read_one_image<T>(s);
        ff_node::ff_send_out(t);
    }
    return this->EOS;
}

// operator for std thread
template <typename T>
void Reader<T>::operator()(
    std::vector<std::string> images,
    std::vector<std::promise<Task<T> *> *> *OutputVector) {

    assert(images.size() >= (*OutputVector).size() &&
           "Error: wrong length for promise vector [Reader::operator()]");
    int count = 0;
    for (std::promise<Task<T> *> *output : *OutputVector) {
        output->set_value(read_one_image<T>(images[count]));
        ++count;
    }
}

template <typename T>
Task<T> *read_one_image(std::string image_name) {
    T *image;
    int32_t rows;
    int32_t cols;

    const std::string &filepath(image_name);

    std::fstream file{filepath, file.binary | file.in};
    if (!file.is_open()) {
        std::cerr << "Error: Failed to open " << filepath << std::endl;
        return NULL;
    } else {
        file.read(reinterpret_cast<char *>(&rows), sizeof(rows));
        file.read(reinterpret_cast<char *>(&cols), sizeof(cols));
        image = new T[rows * cols];
        file.read(reinterpret_cast<char *>(image), (rows * cols) * sizeof(T));
        file.close();
    }

    Task<T> *task;

    std::vector<std::vector<T>> *matrix =
        new std::vector<std::vector<T>>(rows, std::vector<T>(cols));

    for (int x = 0; x < rows; ++x) {
        for (int y = 0; y < cols; ++y) {
            (*matrix)[x][y] = image[x * cols + y];
        }
    }

    task = new Task<T>(matrix, rows, cols, filepath);
    delete[] image;
    return task;
}

#endif /* READER_HPP */
