#include <stdio.h>
#include "libesl_events.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 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 events_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 events_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 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 events_caller(UnifexEnv *env, UnifexCNodeInBuff *in_buff) {
  UNIFEX_MAYBE_UNUSED(in_buff);
  UNIFEX_TERM result;
  char * values;
  values = 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
  values = (char *)malloc(sizeof(char) * size);
  memset(values, 0, size);
  ei_decode_binary(in_buff->buff, in_buff->index, values, &len);
})
) {
  result = unifex_raise(env,
    "Unifex CNode: cannot parse argument 'values' of type ':string'");
  goto exit_events_caller;
}

  result = events(env, values);
  goto exit_events_caller;
  exit_events_caller:
  unifex_free(values);
  return result;
}

int send_esl_event(UnifexEnv * env, UnifexPid pid, int flags, char const * data) {
  UNIFEX_UNUSED(flags);
  ei_x_buff * out_buff = (ei_x_buff *) malloc(sizeof(ei_x_buff));
  ei_x_new_with_version(out_buff);

  ei_x_encode_tuple_header(out_buff, 2);
ei_x_encode_atom(out_buff, "esl_event");
ei_x_encode_binary(out_buff, data, strlen(data));

  unifex_cnode_send_and_free(env, &pid, out_buff);
  return 1;
}


UNIFEX_TERM unifex_cnode_handle_message(UnifexEnv *env, char* fun_name, UnifexCNodeInBuff *in_buff) {
  if (strcmp(fun_name, "connect_timeout") == 0) {
    return connect_timeout_caller(env, in_buff);
  }
 else if (strcmp(fun_name, "events") == 0) {
    return events_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);
}

