#include <stdio.h>
#include "libesl.h"

UnifexState *unifex_alloc_state(UnifexEnv *_env) {
  UNIFEX_UNUSED(_env);
  return (UnifexState *)malloc(sizeof(UnifexState));
}

void unifex_release_state(UnifexEnv *env, UnifexState *state) {
  unifex_cnode_add_to_released_states(env, state);
}

void unifex_cnode_destroy_state(UnifexEnv *env, void *state) {
  handle_destroy_state(env, (UnifexState*)state);
  free(state);
}


UNIFEX_TERM global_set_default_logger_result(UnifexEnv* env) {
  UNIFEX_TERM out_buff = (ei_x_buff *) malloc(sizeof(ei_x_buff));
  unifex_cnode_prepare_ei_x_buff(env, out_buff, "result");

  ei_x_encode_atom(out_buff, "ok");

  return out_buff;
}

UNIFEX_TERM connect_timeout_result(UnifexEnv* env) {
  UNIFEX_TERM out_buff = (ei_x_buff *) malloc(sizeof(ei_x_buff));
  unifex_cnode_prepare_ei_x_buff(env, out_buff, "result");

  ei_x_encode_atom(out_buff, "ok");

  return out_buff;
}

UNIFEX_TERM connect_timeout_result_error(UnifexEnv* env, char const  * atom) {
  UNIFEX_TERM out_buff = (ei_x_buff *) malloc(sizeof(ei_x_buff));
  unifex_cnode_prepare_ei_x_buff(env, out_buff, "result");

  ei_x_encode_tuple_header(out_buff, 2);
ei_x_encode_atom(out_buff, "error");
ei_x_encode_atom(out_buff, atom);

  return out_buff;
}

UNIFEX_TERM send_recv_timed_result_ok(UnifexEnv* env, char const * string) {
  UNIFEX_TERM out_buff = (ei_x_buff *) malloc(sizeof(ei_x_buff));
  unifex_cnode_prepare_ei_x_buff(env, out_buff, "result");

  ei_x_encode_tuple_header(out_buff, 2);
ei_x_encode_atom(out_buff, "ok");
ei_x_encode_binary(out_buff, string, strlen(string));

  return out_buff;
}

UNIFEX_TERM send_recv_timed_result_error(UnifexEnv* env, char const  * atom) {
  UNIFEX_TERM out_buff = (ei_x_buff *) malloc(sizeof(ei_x_buff));
  unifex_cnode_prepare_ei_x_buff(env, out_buff, "result");

  ei_x_encode_tuple_header(out_buff, 2);
ei_x_encode_atom(out_buff, "error");
ei_x_encode_atom(out_buff, atom);

  return out_buff;
}

UNIFEX_TERM global_set_default_logger_caller(UnifexEnv *env, UnifexCNodeInBuff *in_buff) {
  UNIFEX_MAYBE_UNUSED(in_buff);
  UNIFEX_TERM result;
  LoggerLevel level;
  
  if(({
  int res = 1;
  char* enum_as_string = NULL;

  if (!({
enum_as_string = (char *) unifex_alloc(MAXATOMLEN);
ei_decode_atom(in_buff->buff, in_buff->index, enum_as_string);
})
) {
    if (strcmp(enum_as_string, "emerg") == 0) {
  level = LOGGER_LEVEL_EMERG;
  res = 0;
}
 else if (strcmp(enum_as_string, "alert") == 0) {
  level = LOGGER_LEVEL_ALERT;
  res = 0;
}
 else if (strcmp(enum_as_string, "crit") == 0) {
  level = LOGGER_LEVEL_CRIT;
  res = 0;
}
 else if (strcmp(enum_as_string, "error") == 0) {
  level = LOGGER_LEVEL_ERROR;
  res = 0;
}
 else if (strcmp(enum_as_string, "warning") == 0) {
  level = LOGGER_LEVEL_WARNING;
  res = 0;
}
 else if (strcmp(enum_as_string, "notice") == 0) {
  level = LOGGER_LEVEL_NOTICE;
  res = 0;
}
 else if (strcmp(enum_as_string, "info") == 0) {
  level = LOGGER_LEVEL_INFO;
  res = 0;
}
 else if (strcmp(enum_as_string, "debug") == 0) {
  level = LOGGER_LEVEL_DEBUG;
  res = 0;
}


    if (enum_as_string != NULL) {
      unifex_free((void *) enum_as_string);
    }
  }

  res;
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'level' of type ':logger_level'");
  goto exit_global_set_default_logger_caller;
}

  result = global_set_default_logger(env, level);
  goto exit_global_set_default_logger_caller;
  exit_global_set_default_logger_caller:
  
  return result;
}

UNIFEX_TERM connect_timeout_caller(UnifexEnv *env, UnifexCNodeInBuff *in_buff) {
  UNIFEX_MAYBE_UNUSED(in_buff);
  UNIFEX_TERM result;
  char * host;
int port;
char * user;
char * password;
int timeout;
  host = NULL;

user = NULL;
password = NULL;

  if(({
  int type;
  int size;
  long len;
  ei_get_type(in_buff->buff, in_buff->index, &type, &size);
  size = size + 1; // for NULL byte
  host = (char *)malloc(sizeof(char) * size);
  memset(host, 0, size);
  ei_decode_binary(in_buff->buff, in_buff->index, host, &len);
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'host' of type ':string'");
  goto exit_connect_timeout_caller;
}

if(({
  long long tmp_longlong;
  int result = ei_decode_longlong(in_buff->buff, in_buff->index, &tmp_longlong);
  port = (int)tmp_longlong;
  result;
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'port' of type ':int'");
  goto exit_connect_timeout_caller;
}

if(({
  int type;
  int size;
  long len;
  ei_get_type(in_buff->buff, in_buff->index, &type, &size);
  size = size + 1; // for NULL byte
  user = (char *)malloc(sizeof(char) * size);
  memset(user, 0, size);
  ei_decode_binary(in_buff->buff, in_buff->index, user, &len);
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'user' of type ':string'");
  goto exit_connect_timeout_caller;
}

if(({
  int type;
  int size;
  long len;
  ei_get_type(in_buff->buff, in_buff->index, &type, &size);
  size = size + 1; // for NULL byte
  password = (char *)malloc(sizeof(char) * size);
  memset(password, 0, size);
  ei_decode_binary(in_buff->buff, in_buff->index, password, &len);
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'password' of type ':string'");
  goto exit_connect_timeout_caller;
}

if(({
  long long tmp_longlong;
  int result = ei_decode_longlong(in_buff->buff, in_buff->index, &tmp_longlong);
  timeout = (int)tmp_longlong;
  result;
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'timeout' of type ':int'");
  goto exit_connect_timeout_caller;
}

  result = connect_timeout(env, host, port, user, password, timeout);
  goto exit_connect_timeout_caller;
  exit_connect_timeout_caller:
  unifex_free(host);
unifex_free(user);
unifex_free(password);
  return result;
}

UNIFEX_TERM send_recv_timed_caller(UnifexEnv *env, UnifexCNodeInBuff *in_buff) {
  UNIFEX_MAYBE_UNUSED(in_buff);
  UNIFEX_TERM result;
  char * cmd;
int timeout;
  cmd = NULL;

  if(({
  int type;
  int size;
  long len;
  ei_get_type(in_buff->buff, in_buff->index, &type, &size);
  size = size + 1; // for NULL byte
  cmd = (char *)malloc(sizeof(char) * size);
  memset(cmd, 0, size);
  ei_decode_binary(in_buff->buff, in_buff->index, cmd, &len);
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'cmd' of type ':string'");
  goto exit_send_recv_timed_caller;
}

if(({
  long long tmp_longlong;
  int result = ei_decode_longlong(in_buff->buff, in_buff->index, &tmp_longlong);
  timeout = (int)tmp_longlong;
  result;
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'timeout' of type ':int'");
  goto exit_send_recv_timed_caller;
}

  result = send_recv_timed(env, cmd, timeout);
  goto exit_send_recv_timed_caller;
  exit_send_recv_timed_caller:
  unifex_free(cmd);
  return result;
}



UNIFEX_TERM unifex_cnode_handle_message(UnifexEnv *env, char* fun_name, UnifexCNodeInBuff *in_buff) {
  if (strcmp(fun_name, "global_set_default_logger") == 0) {
    return global_set_default_logger_caller(env, in_buff);
  }
 else if (strcmp(fun_name, "connect_timeout") == 0) {
    return connect_timeout_caller(env, in_buff);
  }
 else if (strcmp(fun_name, "send_recv_timed") == 0) {
    return send_recv_timed_caller(env, in_buff);
  }
 else {
  return unifex_cnode_undefined_function_error(env, fun_name);
}

}


int main(int argc, char **argv) {
  return handle_main(argc, argv);
}

