/*
 * This file defines the default C options struct ATC_OPTIONS_DEFAULT
 * and defines a utility function to convert an atc_options C struct to an atc::Options C++ struct.
 */

#include <map>

#include "options.h"
#include "Options.hpp"
#include "util-c.hpp"

static const atc::Options default_cpp_options;

const atc_options ATC_OPTIONS_DEFAULT = {
	
	// General options
	
	.verbose = default_cpp_options.verbose,
	.suppress_warnings = default_cpp_options.suppress_warnings,
	.timings = default_cpp_options.timings,
	.threads = default_cpp_options.threads,
	.storage_order = (default_cpp_options.storage_order == atc::StorageOrder::MSI ? ATC_MSI : ATC_LSI),
	.float_type = (default_cpp_options.float_type == atc::FloatType::float64 ? ATC_FLOAT_TYPE_FLOAT64 : ATC_FLOAT_TYPE_FLOAT32),
	
	// Compression options
	
	// General
	.skip_bytes = default_cpp_options.skip_bytes,
	.error_metric = (default_cpp_options.error_metric == atc::ErrorMetric::RelativeError ? ATC_RELATIVE_ERROR : (default_cpp_options.error_metric == atc::ErrorMetric::RMSE ? ATC_RMSE : ATC_PSNR)),
	
	// T-HODCT
	.apply_dct = default_cpp_options.apply_dct,
	.dct_truncation_max_sse_share = default_cpp_options.dct_truncation_max_sse_share,
	
	// ST-HOSVD
	.rank_truncation_max_sse_share = default_cpp_options.rank_truncation_max_sse_share,
	.mode_processing_order = NULL, /// If not null, assumed to be an array of length modes
	.precise_svd = default_cpp_options.precise_svd,
	.print_squared_singular_values = default_cpp_options.print_squared_singular_values,
	.householder = default_cpp_options.householder,
	
	// Quantization
	.int_type = (default_cpp_options.int_type == atc::IntType::uint64 ? ATC_INT_TYPE_UINT64 : ATC_INT_TYPE_UINT32),
	.core_flattening = (default_cpp_options.core_flattening == atc::CoreFlattening::Unfold ? ATC_UNFOLD : (default_cpp_options.core_flattening == atc::CoreFlattening::Zigzag ? ATC_ZIGZAG : ATC_ZORDER)),
	.mode_storage_order = NULL, /// If not null, assumed to be an array of length modes
	.quantization_blocks = default_cpp_options.quantization_blocks,
	.last_pass_max_threads = default_cpp_options.last_pass_max_threads,
	.quantization_rel_error_margin = default_cpp_options.quantization_rel_error_margin,
	.split_bitplane_truncation = default_cpp_options.split_bitplane_truncation,
	
	// Encoding
	.entropy_coding_method = (default_cpp_options.entropy_coding_method == atc::EntropyCodingMethod::Arithmetic ? ATC_ARITHMETIC : ATC_ANS),
	
	// Decompression options
	.cutout_length = 0,
	.cutout = NULL,
	.autocrop = default_cpp_options.autocrop,
	.autocrop_threshold = default_cpp_options.autocrop_threshold,
	
	// compress_decompress options
	
	.calculate_error = default_cpp_options.calculate_error
	
};

atc::Options convert_options(const atc_options* options, uint8_t modes) {
	
	if (options == NULL)
		throw std::invalid_argument("Received null pointer as options argument!");
	
	atc::Options cpp_options;
	const atc_options& c_options = *options;
	
	// General options
	cpp_options.verbose = c_options.verbose;
	cpp_options.suppress_warnings = c_options.suppress_warnings;
	cpp_options.timings = c_options.timings;
	cpp_options.threads = c_options.threads;
	cpp_options.storage_order = std::map<atc_storage_order, atc::StorageOrder>{
		{ATC_MSI, atc::StorageOrder::MSI},
		{ATC_LSI, atc::StorageOrder::LSI}
	}.at(c_options.storage_order);
	cpp_options.float_type = std::map<atc_float_type, atc::FloatType>{
		{ATC_FLOAT_TYPE_FLOAT32, atc::FloatType::float32},
		{ATC_FLOAT_TYPE_FLOAT64, atc::FloatType::float64}
	}.at(c_options.float_type);
	
	// Compression options
	cpp_options.skip_bytes = c_options.skip_bytes;
	cpp_options.error_metric = std::map<atc_error_metric, atc::ErrorMetric>{
		{ATC_RELATIVE_ERROR, atc::ErrorMetric::RelativeError},
		{ATC_RMSE, atc::ErrorMetric::RMSE},
		{ATC_PSNR, atc::ErrorMetric::PSNR}
	}.at(c_options.error_metric);
	cpp_options.apply_dct = c_options.apply_dct;
	cpp_options.dct_truncation_max_sse_share = c_options.dct_truncation_max_sse_share;
	cpp_options.rank_truncation_max_sse_share = c_options.rank_truncation_max_sse_share;
	if (c_options.mode_processing_order != NULL)
		cpp_options.mode_processing_order = std::vector<uint8_t>(c_options.mode_processing_order, c_options.mode_processing_order + modes);
	cpp_options.precise_svd = c_options.precise_svd;
	cpp_options.print_squared_singular_values = c_options.print_squared_singular_values;
	cpp_options.householder = c_options.householder;
	cpp_options.int_type = std::map<atc_int_type, atc::IntType>{
		{ATC_INT_TYPE_UINT32, atc::IntType::uint32},
		{ATC_INT_TYPE_UINT64, atc::IntType::uint64}
	}.at(c_options.int_type);
	cpp_options.core_flattening = std::map<atc_core_flattening, atc::CoreFlattening>{
		{ATC_UNFOLD, atc::CoreFlattening::Unfold},
		{ATC_ZIGZAG, atc::CoreFlattening::Zigzag},
		{ATC_ZORDER, atc::CoreFlattening::ZOrder}
	}.at(c_options.core_flattening);
	if (c_options.mode_storage_order != NULL)
		cpp_options.mode_storage_order = std::vector<uint8_t>(c_options.mode_storage_order, c_options.mode_storage_order + modes);
	cpp_options.quantization_blocks = c_options.quantization_blocks;
	cpp_options.last_pass_max_threads = c_options.last_pass_max_threads;
	cpp_options.quantization_rel_error_margin = c_options.quantization_rel_error_margin;
	cpp_options.split_bitplane_truncation = c_options.split_bitplane_truncation;
	cpp_options.entropy_coding_method = std::map<atc_entropy_coding_method, atc::EntropyCodingMethod>{
		{ATC_ARITHMETIC, atc::EntropyCodingMethod::Arithmetic},
		{ATC_ANS, atc::EntropyCodingMethod::ANS}
	}.at(c_options.entropy_coding_method);
	
	// Decompression options
	if (c_options.cutout != NULL) {
		cpp_options.cutout = std::vector<atc::Slice>(c_options.cutout_length);
		for (size_t i = 0; i < cpp_options.cutout.size(); i++) {
			const atc_slice& c_slice = c_options.cutout[i];
			// Initialize C++ slice using string description if present, otherwise using other fields
			cpp_options.cutout[i] = (c_slice.description != NULL ?
				atc::Slice(std::string(c_slice.description)) :
				atc::Slice(c_slice.start, c_slice.stop, c_slice.step, std::map<atc_reduction, atc::Reduction>{
					{ATC_DOWNSAMPLING, atc::Reduction::Downsampling},
					{ATC_BOX, atc::Reduction::Box},
					{ATC_LANCZOS, atc::Reduction::Lanczos},
				}.at(c_slice.reduction))
			);
		}
	}
	cpp_options.autocrop = c_options.autocrop;
	cpp_options.autocrop_threshold = c_options.autocrop_threshold;
	
	// compress_decompress options
	cpp_options.calculate_error = c_options.calculate_error;
	
	return cpp_options;
	
}
