/*
 * This file provides the general C++ interface to ATC.
 * This is the main header that should be included by when using the C++ interface,
 * the other  public C++ headers are automatically included as well.
 */

#pragma once

#include <string>
#include <cstdint>
#include <vector>
#include <memory>
#include <utility>

#include "atc/DataType.hpp"
#include "atc/Options.hpp"
#include "atc/Results.hpp"
#include "atc/version.hpp"

/// The ATC namespace, used both by public interface members and internal code.
namespace atc {
	
	/** \addtogroup compress
	 *  @{
	 */
	Results compress(const std::string& original_file, DataType data_type, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const Options& options = Options());
	Results compress(const std::string& original_file, DataType data_type, const std::vector<size_t>& sizes, double target_error, const Options& options = Options());
	template<typename dtype>
	Results compress(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, const Options& options = Options());
	template<typename dtype>
	Results compress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const Options& options = Options());
	/** @} */
	
	/** \addtogroup decompress
	 *  @{
	 */
	Results decompress(const std::string& compressed_file, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results decompress(const std::string& compressed_file, size_t decompressed_size, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> decompress(const std::string& compressed_file, const Options& options = Options());
	Results decompress(size_t compressed_size, const char* compressed_data, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results decompress(size_t compressed_size, const char* compressed_data, size_t decompressed_size, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> decompress(size_t compressed_size, const char* compressed_data, const Options& options = Options());
	Results decompress(const std::vector<char>& compressed_data, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results decompress(const std::vector<char>& compressed_data, size_t decompressed_size, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> decompress(const std::vector<char>& compressed_data, const Options& options = Options());
	/** @} */
	
	/** \addtogroup compdecomp
	 *  @{
	 */
	Results compress_decompress(const std::string& original_file, DataType data_type, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const std::string& original_file, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> compress_decompress(const std::string& original_file, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const Options& options = Options());
	Results compress_decompress(const std::string& original_file, DataType data_type, const std::vector<size_t>& sizes, double target_error, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const std::string& original_file, const std::vector<size_t>& sizes, double target_error, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> compress_decompress(const std::string& original_file, const std::vector<size_t>& sizes, double target_error, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const dtype* original_data,  const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, dtype* decompressed_data, const Options& options = Options());
	/// This function name is more verbose in order to disambiguate it from the next function, which happens to have the same argument types and only differs in its return type.
	template<typename dtype>
	TypedResults<dtype> compress_decompress_to_memory(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> compress_decompress(const dtype* original_data, const std::vector<size_t>& sizes, double target_error, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, dtype* decompressed_data, const Options& options = Options());
	/// This function name is more verbose in order to disambiguate it from the next function, which happens to have the same argument types and only differs in its return type.
	template<typename dtype>
	TypedResults<dtype> compress_decompress_to_memory(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const std::string& compressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const std::string& decompressed_file, const Options& options = Options());
	template<typename dtype>
	Results compress_decompress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, dtype* decompressed_data, const Options& options = Options());
	template<typename dtype>
	TypedResults<dtype> compress_decompress(const std::vector<dtype>& original_data, const std::vector<size_t>& sizes, double target_error, const Options& options = Options());
	/** @} */
	
	/** \addtogroup rdtype
	 *  @{
	 */
	/// Determines the data type of the data represented by a compressed file.
	DataType read_data_type(const std::string& compressed_file);
	/// Determines the data type of the data represented by a compressed output buffer of a given length.
	DataType read_data_type(size_t compressed_size, const char* compressed_data);
	/// Determines the data type of the data represented by a compressed output buffer stored as an `std::vector<char>`.
	DataType read_data_type(const std::vector<char>& compressed_data);
	/** @} */
	
	/** \addtogroup rdsize
	 *  @{
	 */
	/// Determines the number of elements in the decompressed output from a compressed file.
	size_t read_decompressed_size(const std::string& compressed_file, const Options& options = Options());
	/// Determines the number of elements in the decompressed output from a compressed output buffer of a given length.
	size_t read_decompressed_size(size_t compressed_size, const char* compressed_data, const Options& options = Options());
	/// Determines the number of elements in the decompressed output from a compressed output buffer stored as an `std::vector<char>`.
	size_t read_decompressed_size(const std::vector<char>& compressed_data, const Options& options = Options());
	/** @} */
	
	/** \addtogroup cdsize
	 *  @{
	 */
	/// Determines the sizes (i.e. the number of elements) of the decompressed output if a tensor with the given original sizes were to be decompressed with the given decompression options, specifically depending on the given cut-out. Note that when autocrop is enabled, we cannot exactly predict the final decompressed size so we assume nothing is cropped and return a safe upper bound.
	size_t calculate_decompressed_size(const std::vector<size_t>& sizes, const Options& options = Options());
	/** @} */
	
}
