#include <sollya.h>
#include <stdlib.h>
#include <string.h>

int magic1 = 17;
int magic4 = 9999;

void magic_dealloc(void *data) {
  int *ptr;

  ptr = (int *) data;

  sollya_lib_printf("The Sollya object containing the magic with value %d is getting deallocated\n", *ptr);
}

static void __magic_print_and_increment(sollya_obj_t obj) {
  void *data;
  int *ptr;
  
  if (sollya_lib_decompose_externaldata(&data, NULL, obj)) {
    ptr = (int *) data;
    printf("The magic is %d\n", *ptr);
    *ptr = *ptr + 1;
  }
}

int magic_print_and_increment(void **args) {
  __magic_print_and_increment((sollya_obj_t) (args[0]));
  return 1;
}

#define BUF_SIZE ((size_t) 1024)

static void external_data_check_name(char *str, sollya_obj_t obj, char *name1, void *ptr) {
  char name2[BUF_SIZE];
  char actual[BUF_SIZE];

  name2[BUF_SIZE - 1] = '\0';
  actual[BUF_SIZE - 1] = '\0';
  
  sollya_lib_snprintf(name2, BUF_SIZE - 1, "data_%p", ptr);
  sollya_lib_snprintf(actual, BUF_SIZE - 1, "%b", obj);

  if ((strcmp(actual, name1) == 0) ||
      (strcmp(actual, name2) == 0)) {
    sollya_lib_printf("External object %s prints okay.\n", str);
  } else {
    sollya_lib_printf("Unexpected displaying \"%s\" for object %s\n", actual, str);
  }
}

int main(void) {
  int magic2 = 42;
  int magic5 = 23;
  int *magic3, *magic6;
  sollya_obj_t m[6];
  sollya_obj_t pni, temp;
  sollya_externalprocedure_type_t argTypes[] = { SOLLYA_EXTERNALPROC_TYPE_OBJECT };
  int i, j;

  magic3 = (int *) malloc(sizeof(int));
  if (magic3 == NULL) return 1;
  magic6 = (int *) malloc(sizeof(int));
  if (magic6 == NULL) return 1;
  
  sollya_lib_init();

  *magic3 = 1664;
  *magic6 = 1001;

  m[0] = sollya_lib_externaldata(NULL, &magic1, magic_dealloc);
  m[1] = sollya_lib_externaldata(NULL, &magic2, magic_dealloc);
  m[2] = sollya_lib_externaldata(NULL,  magic3, magic_dealloc);
  m[3] = sollya_lib_externaldata("zauberei1", &magic4, magic_dealloc);
  m[4] = sollya_lib_externaldata("zauberei2", &magic5, magic_dealloc);
  m[5] = sollya_lib_externaldata("zauberei3",  magic6, magic_dealloc);

  pni = sollya_lib_externalprocedure(SOLLYA_EXTERNALPROC_TYPE_VOID, argTypes, 1, NULL, magic_print_and_increment);

  external_data_check_name("m[0]", m[0], "magic1", &magic1);
  external_data_check_name("m[1]", m[1], "magic2", &magic2);
  external_data_check_name("m[2]", m[2], "magic3",  magic3);
  external_data_check_name("m[3]", m[3], "zauberei1", &magic4);
  external_data_check_name("m[4]", m[4], "zauberei2", &magic5);
  external_data_check_name("m[5]", m[5], "zauberei3",  magic6);

  for (j=0;j<3;j++) {
    sollya_lib_printf("%dth run:\n", j);
    for (i=0;i<6;i++) {
      sollya_lib_printf("Printing and incrementing the external data object m[%d]:\n", i);
      temp = sollya_lib_apply(pni, m[i], NULL);
      sollya_lib_printf("Done with m[%d]\n", i);
      sollya_lib_clear_obj(temp);
    }
  }
  
  for (i=0;i<6;i++) {
    sollya_lib_clear_obj(m[i]);  
  }
  sollya_lib_clear_obj(pni);
  
  sollya_lib_close();

  free(magic3);
  free(magic6);
  
  return 0;
}

