#include "CTfLiteClass.h"

// #include "bitmap_image.hpp"

#include "ClassLogFile.h"
#include "Helper.h"

#include <sys/stat.h>

bool debugdetailtflite = false;

float CTfLiteClass::GetOutputValue(int nr)
{
    TfLiteTensor* output2 = this->interpreter->output(0);

    int numeroutput = output2->dims->data[1];
    if ((nr+1) > numeroutput)
      return -1000;

    return output2->data.f[nr];
}

int CTfLiteClass::GetClassFromImageBasis(CImageBasis *rs)
{
//  printf("Before Load image %s\n", _fn.c_str());
    if (!LoadInputImageBasis(rs))
      return -1000;

    Invoke();
    printf("After Invoke \n");

    return GetOutClassification();
}

int CTfLiteClass::GetOutClassification()
{
  TfLiteTensor* output2 = interpreter->output(0);

  float zw_max = 0;
  float zw;
  int zw_class = -1;

  if (output2 == NULL)
    return -1;

  int numeroutput = output2->dims->data[1];
  for (int i = 0; i < numeroutput; ++i)
  {
    zw = output2->data.f[i];
    if (zw > zw_max)
    {
        zw_max = zw;
        zw_class = i;
    }
  }
//  printf("Result Ziffer: %d\n", zw_class);       
  return zw_class;
}

void CTfLiteClass::GetInputDimension(bool silent = false)
{
  TfLiteTensor* input2 = this->interpreter->input(0);

  int numdim = input2->dims->size;
  if (!silent)  printf("NumDimension: %d\n", numdim);  

  int sizeofdim;
  for (int j = 0; j < numdim; ++j)
  {
    sizeofdim = input2->dims->data[j];
    if (!silent) printf("SizeOfDimension %d: %d\n", j, sizeofdim);  
    if (j == 1) im_height = sizeofdim;
    if (j == 2) im_width = sizeofdim;
    if (j == 3) im_channel = sizeofdim;
  }
}


void CTfLiteClass::GetOutPut()
{
  TfLiteTensor* output2 = this->interpreter->output(0);

  int numdim = output2->dims->size;
  printf("NumDimension: %d\n", numdim);  

  int sizeofdim;
  for (int j = 0; j < numdim; ++j)
  {
    sizeofdim = output2->dims->data[j];
    printf("SizeOfDimension %d: %d\n", j, sizeofdim);  
  }


  float fo;

  // Process the inference results.
  int numeroutput = output2->dims->data[1];
  for (int i = 0; i < numeroutput; ++i)
  {
   fo = output2->data.f[i];
    printf("Result %d: %f\n", i, fo);  
  }
}

void CTfLiteClass::Invoke()
{
    interpreter->Invoke();
//    printf("Invoke Done.\n");
}



bool CTfLiteClass::LoadInputImageBasis(CImageBasis *rs)
{
    std::string zw = "ClassFlowAnalog::doNeuralNetwork nach LoadInputResizeImage: ";

    unsigned int w = rs->width;
    unsigned int h = rs->height;
    unsigned char red, green, blue;
//    printf("Image: %s size: %d x %d\n", _fn.c_str(), w, h);

    input_i = 0;
    float* input_data_ptr = (interpreter->input(0))->data.f;

    for (int y = 0; y < h; ++y)
        for (int x = 0; x < w; ++x)
            {
                red = rs->GetPixelColor(x, y, 0);
                green = rs->GetPixelColor(x, y, 1);
                blue = rs->GetPixelColor(x, y, 2);
                *(input_data_ptr) = (float) red;
                input_data_ptr++;
                *(input_data_ptr) = (float) green;
                input_data_ptr++;
                *(input_data_ptr) = (float) blue;
                input_data_ptr++;
            }
    
    if (debugdetailtflite) LogFile.WriteToFile("Nach dem Laden in input");

    return true;
}


void CTfLiteClass::MakeAllocate()
{
    static tflite::AllOpsResolver resolver;
    this->interpreter = new tflite::MicroInterpreter(this->model, resolver, this->tensor_arena, this->kTensorArenaSize, this->error_reporter);

    TfLiteStatus allocate_status = this->interpreter->AllocateTensors();
    if (allocate_status != kTfLiteOk) {
        TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
    this->GetInputDimension();   
    return;
  }
//    printf("Allocate Done.\n");
}

void CTfLiteClass::GetInputTensorSize(){
    float *zw = this->input;
    int test = sizeof(zw);
    printf("Input Tensor Dimension: %d\n", test);       
}

long CTfLiteClass::GetFileSize(std::string filename)
{
    struct stat stat_buf;
    long rc = stat(filename.c_str(), &stat_buf);
    return rc == 0 ? stat_buf.st_size : -1;
}


unsigned char* CTfLiteClass::ReadFileToCharArray(std::string _fn)
{
    long size;
    
    size = GetFileSize(_fn);

    if (size == -1)
    {
		printf("\nFile existiert nicht.\n");
        return NULL;
    }

    unsigned char *result = (unsigned char*) malloc(size);
    int anz = 1;
    TickType_t xDelay;
    while (!result && (anz < 6))    // maximal 5x versuchen (= 5s)
    {
		    printf("Speicher ist voll - Versuche es erneut: %d.\n", anz);
        xDelay = 1000 / portTICK_PERIOD_MS;
        result = (unsigned char*) malloc(size);
        anz++;
    }

  
	  if(result != NULL) {
//		printf("\nSpeicher ist reserviert\n");
        FILE* f = OpenFileAndWait(_fn.c_str(), "rb");     // vorher  nur "r"
        fread(result, 1, size, f);
        fclose(f);        
	  }else {
		  printf("\nKein freier Speicher vorhanden.\n");
	}    


    return result;
}

void CTfLiteClass::LoadModel(std::string _fn){

#ifdef SUPRESS_TFLITE_ERRORS
    this->error_reporter = new tflite::OwnMicroErrorReporter;
#else
    this->error_reporter = new tflite::MicroErrorReporter;
#endif

    unsigned char *rd;
    rd = ReadFileToCharArray(_fn.c_str());

    this->model = tflite::GetModel(rd);
    free(rd);
    TFLITE_MINIMAL_CHECK(model != nullptr); 
}



CTfLiteClass::CTfLiteClass()
{
    this->model = nullptr;
    this->interpreter = nullptr;
    this->input = nullptr;
    this->output = nullptr;  
    this->kTensorArenaSize = 150 * 1024;   /// laut testfile: 108000 - bisher 600
    this->tensor_arena = new uint8_t[kTensorArenaSize]; 
}

CTfLiteClass::~CTfLiteClass()
{
  delete this->tensor_arena;
  delete this->interpreter;
  delete this->error_reporter;
}        


namespace tflite {

  int OwnMicroErrorReporter::Report(const char* format, va_list args) {
    return 0;
  }

}  


