/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

// -*- c++ -*-

/***********************************************************
 * Abstract I/O objects
 *
 * I/O is always sequential, seek does not need to be supported
 * (indexes could be read or written to a pipe).
 ***********************************************************/

#pragma once

#include <string>
#include <cstdio>
#include <vector>

#include <faiss/Index.h>

namespace faiss {


struct IOReader {
    // name that can be used in error messages
    std::string name;

    // fread. Returns number of items read or 0 in case of EOF.
    virtual size_t operator()(
         void *ptr, size_t size, size_t nitems) = 0;

    // return a file number that can be memory-mapped
    virtual int fileno ();

    virtual ~IOReader() {}
};

struct IOWriter {
    // name that can be used in error messages
    std::string name;

    // fwrite. Return number of items written
    virtual size_t operator()(
         const void *ptr, size_t size, size_t nitems) = 0;

    // return a file number that can be memory-mapped
    virtual int fileno ();

    virtual ~IOWriter() noexcept(false) {}
};


struct VectorIOReader:IOReader {
    std::vector<uint8_t> data;
    size_t rp = 0;
    size_t operator()(void *ptr, size_t size, size_t nitems) override;
};

struct VectorIOWriter:IOWriter {
    std::vector<uint8_t> data;
    size_t operator()(const void *ptr, size_t size, size_t nitems) override;
};

struct FileIOReader: IOReader {
    FILE *f = nullptr;
    bool need_close = false;

    FileIOReader(FILE *rf);

    FileIOReader(const char * fname);

    ~FileIOReader() override;

    size_t operator()(void *ptr, size_t size, size_t nitems) override;

    int fileno() override;
};

struct FileIOWriter: IOWriter {
    FILE *f = nullptr;
    bool need_close = false;

    FileIOWriter(FILE *wf);

    FileIOWriter(const char * fname);

    ~FileIOWriter() override;

    size_t operator()(const void *ptr, size_t size, size_t nitems) override;

    int fileno() override;
};

/*******************************************************
 * Buffered reader + writer
 *
 * They attempt to read and write only buffers of size bsz to the
 * underlying reader or writer. This is done by splitting or merging
 * the read/write functions.
 *******************************************************/



/** wraps an ioreader to make buffered reads to avoid too small reads */
struct BufferedIOReader: IOReader {

    IOReader *reader;
    size_t bsz;
    size_t ofs;    ///< offset in input stream
    size_t ofs2;   ///< number of bytes returned to caller
    size_t b0, b1; ///< range of available bytes in the buffer
    std::vector<char> buffer;

    /**
     * @param bsz    buffer size (bytes). Reads will be done by batched of
     *               this size
     */
    explicit BufferedIOReader(IOReader *reader, size_t bsz = 1024 * 1024);

    size_t operator()(void *ptr, size_t size, size_t nitems) override;
};


struct BufferedIOWriter: IOWriter {

    IOWriter *writer;
    size_t bsz;
    size_t ofs;
    size_t ofs2;     ///< number of bytes received from caller
    size_t b0;       ///< amount of data in buffer
    std::vector<char> buffer;

    explicit BufferedIOWriter(IOWriter *writer, size_t bsz = 1024 * 1024);

    size_t operator()(const void *ptr, size_t size, size_t nitems) override;

    // flushes
    ~BufferedIOWriter() override;
};

/// cast a 4-character string to a uint32_t that can be written and read easily
uint32_t fourcc (const char sx[4]);
uint32_t fourcc (const std::string & sx);

// decoding of fourcc (int32 -> string)
void fourcc_inv(uint32_t x, char str[5]);
std::string fourcc_inv(uint32_t x);
std::string fourcc_inv_printable(uint32_t x);


} // namespace faiss
