/* ***************************************************************************+
 * ITX package (cnrg.itx) for telephony application programming.              *
 * Copyright (c) 1999  Cornell University, Ithaca NY.                         *
 * A copy of the license is distributed with this package.  Look in the docs  *
 * directory, filename GPL.  Contact information: bergmark@cs.cornell.edu     *
 ******************************************************************************/
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <jni.h>
#include "arpa/nameser.h"
#include "resolv.h"
#include "cnrg_itx_ds_DSComm.h"

/* !!!! caller should remember to free the pointer returned after he is done */
char **GetRData(ns_msg *handle, ns_sect section, int pflag, int *nEntries) {
  int len, sflag, rrnum, rdlen;
  const u_char *rdata, *edata;
  char *buf;
  ns_rr rr;
  ns_type type;
  char **pptr;
  int index;
  
  /*
   * Print answer records.
   */
  sflag = (_res.pfcode & pflag);
  if (_res.pfcode && !sflag)
    return NULL;
  
  /* how many record returned */
  rrnum = ns_msg_count(*handle, ns_s_an);
#ifdef DEBUG
  fprintf(stderr, "DEBUG: Number of record returned: %d\n", rrnum);
#endif
  *nEntries = rrnum;
  
  /* malloc array of array of pointers */
  pptr = (char**) malloc(sizeof(char*)*rrnum);
  /*for (i=0; i<4; i++) {
    (char*)pptr[i] = (char *) malloc (sizeof(char)*5);
    memset((char*)pptr[i], '\0', 5);
    fprintf(stderr, "DEBUG: size of pptr[%d] is: %d\n", i, strlen(pptr[i]));
    }*/
  
  for (index=0; index<rrnum; index++) {
    
    if (ns_parserr(handle, section, index, &rr)) {
      if (errno != ENODEV)
	fprintf(stderr, ";; ns_parserr: %s\n", strerror(errno));
      continue;
    }
    
    type = (ns_type) ns_rr_type(rr);
    if (type != ns_t_txt)
      continue;
    rdata = ns_rr_rdata(rr);
    if (!rdata)
      continue;
    rdlen = ns_rr_rdlen(rr);
    buf = (char *)malloc(rdlen);
    if (!buf)
      continue;
    
    strncpy(buf, (const char*)rdata+1, *rdata);
    buf[*rdata] = NULL;
    
    pptr[index] = buf;
    buf = NULL;
  }
  
  return pptr;
}

int DeleteRecord(const char *key, const char *txt, int len)
{
  ns_updrec *urec;
  u_char *data;

  res_init();
  urec = res_mkupdrec(S_UPDATE, key, C_IN, T_TXT, 0);
  urec->r_opcode = DELETE;

  if (len == -1) {
    urec->r_data = NULL;
    urec->r_size = 0;
  }
  else {
    data = (u_char*) malloc(len+1);
    *data = len;
    strcpy((char*)data+1, txt);
    urec->r_data = data;
    urec->r_size = len+1;
  }
  if (res_update(urec) < 0) {
    perror("failed update");
    return -1;
  }

  free(urec->r_dname);
  if (urec->r_data)
    free(urec->r_data);
  free(urec);
  return 0;
}

int AddRecord(const char *name, const char *txt, int len)
{
  ns_updrec *urec;
  u_char *data;

  res_init();
  urec = res_mkupdrec(S_UPDATE, name, C_IN, T_TXT, 0);
  urec->r_opcode = ADD;

  data = (u_char*) malloc(len+1);
  *data = len;
  strcpy((char*)data+1, txt);
  urec->r_data = data;
  urec->r_size = len+1;

  if (res_update(urec) < 0) {
    perror("failed update");
    return -1;
  }

  free(urec->r_dname);
  if (urec->r_data)
    free(urec->r_data);
  free(urec);
  return 0;
}

/* !!!! caller should free the pointer returned when done */
char **GetRecordByName(const char *name, int *nEntries)
{
  union {
    HEADER hdr;
    u_char buf[NS_PACKETSZ];
  } response;
  int responseLen;
  ns_msg handle;
  char **txt;

  res_init();
  responseLen = res_search(name, ns_c_in, ns_t_txt,
			  (u_char *) &response, sizeof(response));
  if (responseLen < 0) {
#ifdef DEBUG
    fprintf(stderr, "DEBUG: no record found\n");
#endif
    return NULL;
  }
#ifdef DEBUG
  fprintf(stderr, "DEBUG: responseLen: %i\n", responseLen);
#endif

  if (ns_initparse(response.buf, responseLen, &handle) < 0) {
    fprintf(stderr, ";; ns_initparse: %s\n", strerror(errno));
    return NULL;
  }

  txt = GetRData(&handle, ns_s_an, RES_PRF_ANS, nEntries);
/*  if (!txt) {
    fprintf(stderr, "DEBUG: error getting rdata\n");
    return NULL;
  }
  else if (nEntry == 0) {
    fprintf(stderr, "DEBUG: No entry found\n");
    return NULL;
  }*/
    
  return txt;
}


/*
 * Class:     cnrg_itx_ds_DSComm
 * Method:    addRecord
 * Signature: ([B[B)V
 */
JNIEXPORT void JNICALL Java_cnrg_itx_ds_DSComm_addRecord
  (JNIEnv *jenv, jobject jobj, jbyteArray jentry, jbyteArray jrecord) {

  jboolean iscopy,isrec;
  jsize lenentry = (*jenv)->GetArrayLength(jenv, jentry);
  jsize lenrecord = (*jenv)->GetArrayLength(jenv, jrecord);
  jbyte *bodyentry = (*jenv)->GetByteArrayElements(jenv, jentry, &iscopy);
  jbyte *bodyrecord = (*jenv)->GetByteArrayElements(jenv, jrecord, &isrec);

#ifdef _DEBUG
   fprintf(stderr, "DEBUG: addRecord input: %s, %s\n", (char *)bodyentry,
   (char *)bodyrecord);
#endif

  AddRecord((char *)bodyentry, (char *)bodyrecord, strlen((char *)bodyrecord));

  /* free up memory */
  if (iscopy == JNI_TRUE)
     (*jenv)->ReleaseByteArrayElements(jenv, jentry, bodyentry, 0);
  if (isrec == JNI_TRUE)
     (*jenv)->ReleaseByteArrayElements(jenv, jrecord, bodyrecord, 0);
}


/*
 * Class:     cnrg_itx_ds_DSComm
 * Method:    deleteRecord
 * Signature: ([B[B)V
 */
JNIEXPORT void JNICALL Java_cnrg_itx_ds_DSComm_deleteRecord
  (JNIEnv *jenv, jobject jobj, jbyteArray jentry, jbyteArray jrecord) {

  jsize lenentry, lenrecord;
  jbyte *bodyentry, *bodyrecord;
  jboolean iscopy,isrec;

  lenentry = (*jenv)->GetArrayLength(jenv, jentry);
  bodyentry = (*jenv)->GetByteArrayElements(jenv, jentry, &iscopy);

  if (jrecord) {
    lenrecord = (*jenv)->GetArrayLength(jenv, jrecord);
    bodyrecord = (*jenv)->GetByteArrayElements(jenv, jrecord, &isrec);

#ifdef _DEBUG
   fprintf(stderr, "DEBUG: deleteRecord input: %s, %s\n", (char *)bodyentry,
      (char *)bodyrecord);
#endif

    DeleteRecord((char *)bodyentry, (char *)bodyrecord, strlen((char *)bodyrecord));
  }else{
#ifdef _DEBUG
   fprintf(stderr, "DEBUG: deleteRecord input: %s, null \n", (char *)bodyentry);
#endif
    DeleteRecord((char *)bodyentry, NULL, -1);
  }

  /* free up memory */
  if (iscopy == JNI_TRUE)
    (*jenv)->ReleaseByteArrayElements(jenv, jentry, bodyentry, 0);
  if (isrec == JNI_TRUE)
    (*jenv)->ReleaseByteArrayElements(jenv, jrecord, bodyrecord, 0);
}


/*
 * Class:     cnrg_itx_ds_DSComm
 * Method:    getRecord
 * Signature: ([B)Lcnrg/itx/ds/ArrayRecords;
 */
JNIEXPORT jobject JNICALL Java_cnrg_itx_ds_DSComm_getRecord
  (JNIEnv *jenv, jobject jobj, jbyteArray jentry) {

  jboolean iscopy;
  int index=0;
  int nSize=0;
  char **pArr;
  jsize lenentry = (*jenv)->GetArrayLength(jenv, jentry);
  jbyte *bodyentry = (*jenv)->GetByteArrayElements(jenv, jentry, &iscopy);

  /* create ArrayRecords object 
     Use "javap -s -p cnrg/itx/ds/ArrayRecords" command to get all methods signiture */
  jclass jstr = (*jenv)->FindClass(jenv, "cnrg/itx/ds/ArrayRecords");
  jmethodID jmIDinit = (*jenv)->GetMethodID(jenv, jstr, "<init>", "()V");
  jmethodID jmIDadd = (*jenv)->GetMethodID(jenv, jstr, "add", "(Ljava/lang/String;)V");
  jobject jarr = (*jenv)->NewObject(jenv, jstr, jmIDinit);

#ifdef DEBUG
  fprintf(stderr, "DEBUG: getRecord input: %s\n", (char *)bodyentry);
#endif

  pArr = GetRecordByName((char *)bodyentry, &nSize);

  /* set jarr elements */
  for (index=0; index<nSize; index++) {
    jstring jnewStr = (*jenv)->NewStringUTF(jenv, (char*)(pArr[index]));
    free(pArr[index]);

    (*jenv)->CallVoidMethod(jenv, jarr, jmIDadd, jnewStr);
  }
  
  /* free up memory */
  if (iscopy == JNI_TRUE)
     (*jenv)->ReleaseByteArrayElements(jenv, jentry, bodyentry, 0);
  free (pArr);

  return jarr;
}


/*
 * Class:     cnrg_itx_ds_DSComm
 * Method:    setConfigPath
 * Signature: ([B)V
 */
JNIEXPORT void JNICALL Java_cnrg_itx_ds_DSComm_setConfigPath
  (JNIEnv *jenv, jobject jobj, jbyteArray jpath) {

  jboolean iscopy;
  jsize len = (*jenv)->GetArrayLength(jenv, jpath);
  jbyte *body = (*jenv)->GetByteArrayElements(jenv, jpath, &iscopy);

#ifdef _DEBUG
   fprintf(stderr, "DEBUG: setConfigPath input: %s\n", (char *)body);
#endif

  init_res_paths_by_filename((char *)body);

  /* free up memory */
  if (iscopy == JNI_TRUE)
     (*jenv)->ReleaseByteArrayElements(jenv, jpath, body, 0);
}
