// Header guards
#ifndef UTILS_HEADER
#define UTILS_HEADER



#include "tensor.h"
#include <vector>
#include <stdlib.h>


std::vector<float> run_accuracies;
std::string model_params_path = "../../test/dnn_benchmarks/model_params/";



__attribute__((noinline)) struct Tensor *readTrainedWeights(const char *file_name, int data_type,
							    long int dim1_size, long int dim2_size,
							    long int dim3_size, long int dim4_size) {
  
  int type_size = 4; // NOTE: Assuming floating point tensors
  long int num_elems = dim1_size * dim2_size * dim3_size * dim4_size;
  long int size_in_bytes =
      type_size * dim1_size * dim2_size * dim3_size * dim4_size;
  float *tensor_data = (float *)malloc(sizeof(float) * num_elems);
 
  int file_header_size = 0;

  FILE *file = fopen(file_name, "rb");
  if (file == NULL) {
    printf("Data file %s is not found. Aborting... \n", file_name);
    abort();
  }

  fseek(file, file_header_size, SEEK_CUR); // Skipping the file header
  size_t bytes_read = fread(tensor_data, 1, size_in_bytes, file);

  fclose(file);


  // Returning a dummy tensor 
  struct Tensor* weightsTensor = new struct Tensor;
  return weightsTensor;
}


__attribute__((noinline)) struct Tensor *readInputBatch(const char *file_name, long data_type,
							long start, long end,
							long dim2_size, long dim3_size, long dim4_size) {
  long int dim1_size = end - start;
  // FIXIT: Don't assume floating point types
  long int type_size = 4; // NOTE: Assuming floating point tensors
  long int num_elems = dim1_size * dim2_size * dim3_size * dim4_size;
  long int size_in_bytes =
      type_size * dim1_size * dim2_size * dim3_size * dim4_size;
  float *tensor_data = (float *)malloc(sizeof(float) * num_elems);
  long int file_header_size =
      type_size * start * dim2_size * dim3_size * dim4_size;

  FILE *file = fopen(file_name, "rb");
  if (file == NULL) {
    printf("Data file %s is not found. Aborting... \n", file_name);
    abort();
  }

  fseek(file, file_header_size, SEEK_SET); // Skipping the file header
  size_t bytes_read = fread(tensor_data, 1, size_in_bytes, file);

  fclose(file);

  // Creating and returning empty tensor
  struct Tensor* inputTensor = new struct Tensor;
  return inputTensor;
}

__attribute__((noinline)) uint8_t *readLabels(const char *labels_file, int num_labels) {

  uint8_t *labels = (uint8_t *)malloc(sizeof(uint8_t) * num_labels);
  FILE *file = fopen(labels_file, "rb");
  if (file == NULL) {
    printf("Data file %s is not found. Aborting...\n", labels_file);
    abort();
  }

  size_t bytes_read = fread(labels, 1, sizeof(uint8_t) * num_labels, file);

  fclose(file);
  
  return labels;	 
}

__attribute__((noinline)) uint32_t *readLabels3(const char *labels_file, int num_labels) {

  uint32_t *labels = (uint32_t *) malloc(sizeof(uint32_t) * num_labels);
  FILE *file = fopen(labels_file, "rb");
  if (file == NULL) {
    printf("Data file %s is not found. Aborting...\n", labels_file);
    abort();
  }

  size_t bytes_read = fread(labels, 1, sizeof(uint32_t) * num_labels, file);
  fclose(file);

  return labels; 
}


__attribute__((noinline)) uint32_t *readLabelsBatch3(const char *labels_file, int start, int end) {

  long int num_labels = end - start;
  uint32_t *labels = (uint32_t *) malloc(sizeof(uint32_t) * num_labels);
  FILE *file = fopen(labels_file, "rb");
  if (file == NULL) {
    printf("Data file %s is not found. Aborting...\n", labels_file);
    abort();
  }

  size_t bytes_read = fread(labels, 1, sizeof(uint32_t) * num_labels, file);
  fclose(file);

  return labels; 
}



float computeAccuracy3(uint32_t *labels, void *result_ptr) {

  return 100.0; // dummy return  
}


// tensor_runtime.h empty definitions - for NVDLA-based compilation to work (functions not actually used)


__attribute__((noinline)) void *create4DTensor(int data_type, int data_format, size_t dim1_size,
					       size_t dim2_size, size_t dim3_size, size_t dim4_size){

  long int type_size = 4; // NOTE: Assuming floating point tensors
  long int num_elems = dim1_size * dim2_size * dim3_size * dim4_size;
  long int size_in_bytes =
      type_size * dim1_size * dim2_size * dim3_size * dim4_size;
  
  struct Tensor* weightsTensor = new struct Tensor;
  return weightsTensor;
}


void startMemTracking(){
}
  
void freeBatchMemory(){

}

void hpvm_request_tensor(void *tensor, int destination){

}


void llvm_hpvm_initializeRuntimeController(const char *){

}


void llvm_hpvm_invokeRtControl(void *result,
			       const char *str,
			       int start,
                               int end){


}



#endif