/*
 * This file handles the private implementation of the atc_results C struct and its connection to the atc::Results C++ struct,
 * including a utility conversion function and the definition of the destructor atc_destroy_results.
 */

#include <iostream>

#include "atc.h"
#include "atc.hpp"
#include "util-c.hpp"

/**
 * We transform the stack-allocated C++ results to heap-allocated C results by keeping the data in the corresponding C++ containers
 * Because these can't be seen by the C code we only store an opaque pointer to atc_results_impl
 * Then, we can simply allocate/deallocate atc_results_impl on the heap and set some pointers to make the data available to C code,
 * without needing to show the internal implementation through C++ containers
 * The main benefit of this approach is that we don't copy compressed_data, we just move it
 */
typedef struct atc_results_impl {
	atc::Results results;
	std::vector<atc_timing> timings;
} atc_results_impl;

atc_results convert_results(atc::Results&& cpp_results) {
	
	// Move stack-allocated atc::Results to equivalent atc_results_impl
	atc_results_impl* heap_allocated_results = new atc_results_impl();
	heap_allocated_results->results = std::move(cpp_results);
	atc::Results& moved_cpp_results = heap_allocated_results->results;
	// Construct timings vector which reuses strings stored in timings map
	heap_allocated_results->timings.resize(moved_cpp_results.timings.size());
	size_t index = 0;
	for (auto& [name, timing] : moved_cpp_results.timings)
		heap_allocated_results->timings[index++] = {name.c_str(), timing};
	
	// Create c_results and link them to the hidden implementation atc_results_impl
	atc_results c_results;
	c_results.success = true; // If the ATC call throws any exceptions, a new empty atc_results object will be returned anyway, so in case this object is used it definitely comes from a successful call
	c_results.compressed_size = moved_cpp_results.compressed_size;
	c_results.compressed_data = moved_cpp_results.compressed_data.data();
	c_results.compression_factor = moved_cpp_results.compression_factor;
	c_results.decompressed_sizes_length = moved_cpp_results.decompressed_sizes.size();
	c_results.decompressed_sizes = moved_cpp_results.decompressed_sizes.data();
	c_results.error = moved_cpp_results.error;
	c_results.timings_length = heap_allocated_results->timings.size();
	c_results.timings = heap_allocated_results->timings.data();
	c_results.impl = heap_allocated_results;
	
	return c_results;
	
}

void atc_destroy_results(atc_results* results) {
	if (results->impl == NULL) {
		/// If the results come from a failed ATC call, the user should already have received a warning from that and the following warning can be skipped
		if (results->success)
			warn("atc_results->impl was already equal to NULL when calling atc_destroy_results, indicating atc_results was already destroyed earlier. As a result no action was performed.");
	} else {
		// Delete heap-allocated hidden results implementation, then reset state of atc_results object to make sure all dangling pointers are removed
		delete results->impl;
		*results = atc_results();
	}
}
