/**
 * This does the query execution for the new mongodb based sicslog. 
 * 
 * COPYRIGHT GPL
 *
 * Mark Koennecke, February 2016
 */
#include <sicslogquery.h>
#include <unistd.h>
#include <sys/time.h>
#include <logv2.h>
#include <approxidate.h>

static char lastError[1024];
static char mongoURL[1024];
static char inst[132];

static mongoc_client_t *client;
static mongoc_collection_t *collection;

/*--------------------------------------------------------------------------------*/
char * sicslogGetError(void)
{
  /*
    May be change to strdup(lastError);
  */
  return lastError;
}
/*--------------------------------------------------------------------------------*/
int sicslogSetup(char *url, char *instPar)
{
  memset(inst,0,sizeof(inst));

  if(instPar != NULL){
    strncpy(inst,instPar, sizeof(inst));
  }
  strncpy(mongoURL,url,sizeof(mongoURL));

  mongoc_init();
  client = mongoc_client_new (mongoURL);
  if(!client){
    snprintf(lastError,sizeof(lastError),"ERROR: failed to connect to mongodb, bad URL?");
    return 1;
  }
  return 0;
}
/*--------------------------------------------------------------------------------*/
static unsigned int sevFromText(const char *txt)
{
  static const char *severityText[] = {"fatal",
			       "error",
                               "warn",
                               "info",
                               "verbose",
                               "debug",
                               NULL
                                };
  int sev = 0;
  while(severityText[sev] != NULL){
    if(strcmp(txt,severityText[sev]) == 0){
      break;
    }
    sev++;
  }
  sev++; /* starting at 1 rather then 0 */
  return sev;
}
/*---------------------------------------------------------------------------*/
int sicslogQuery(int argc, char *argv[], ResultCallback_t func, void *userData)
{
  char c;
  time_t to = time(NULL), from = to - 3600;
  int severity = INFO, mult = 1, status = 0;
  char *sub = NULL;
  char *exp = NULL;
  char *instLocal = NULL;
  char *tmp, *pPtr;
  char jsonQuery[2024], subQuery[132];
  struct timeval tv;
  mongoc_collection_t *collection = NULL;
  bson_t *query;
  const bson_t *doc;
  mongoc_cursor_t *cursor; 
  bson_error_t  err;

  /*
    parse options
  */
  optind = 1;
  while((c = getopt(argc,argv,"s:l:f:t:i:e:c:h")) != -1) {
    switch (c){
    case 's':
      sub = strdup(optarg);
      break;
    case 'l':
      tmp = strdup(optarg);
      if((pPtr = strrchr(tmp,(int)'h')) != NULL){
	mult = 60;
	*pPtr = '\0';
      } else if((pPtr = strrchr(tmp,(int)'d')) != NULL){
	mult = 24*60;
	*pPtr = '\0';
      }
      from = to - atoi(tmp)*mult*60;
      free(tmp);
      break;
    case 'f':
      if(approxidate(optarg,&tv) == 0){
	from = tv.tv_sec;
      } else {
	snprintf(lastError,sizeof(lastError),"Failed to parse date from %s", optarg);
	return 1;
      }
      break;
    case 't':
      if(approxidate(optarg,&tv) == 0){
	to = tv.tv_sec;
      } else {
	snprintf(lastError,sizeof(lastError),"Failed to parse date from %s", optarg);
	return 1;
      }
      break;
    case 'i':
      instLocal = strdup(optarg);
      break;
    case 'e':
      exp = strdup(optarg);
      break;
    case 'c':
      severity = sevFromText(optarg);
      break;
    case 'h':
      snprintf(
	       lastError,sizeof(lastError),
	       "Options: \n-s subsystem\n-l last(h,d)\n-f fromtime\n-t totime\n-i instrument\n-c severity\n%s",
	       "-e search-expression"); 
      return 1;
      break;
    case '?':
      if( optopt == 's' || optopt == 'l' || optopt == 'f' || optopt == 't' 
	  || optopt == 'i' || optopt == 'e' || optopt == 'c') {
	snprintf(lastError,sizeof(lastError),"Option %c requires an argument", optopt);
      } else {
	snprintf(lastError,sizeof(lastError),"Unknown option %c", optopt);      
      }
      return 1;
      break;
    }
  }

  if(instLocal == NULL && strlen(inst) > 1){
    instLocal = strdup(inst);
  }
  if(instLocal == NULL){
	snprintf(lastError,sizeof(lastError),"Do not know which instrument log to query, specify with -i");
	return 1;
  }

  /*
    build the query
  */
  snprintf(jsonQuery, sizeof(jsonQuery),"{ \"timestamp\" : {\"$gt\": %ld, \"$lt\": %ld}, \"severity\": {\"$lte\": %d}", 
	   from,to,severity);
  if(sub != NULL){
    snprintf(subQuery,sizeof(subQuery),", \"sub\" : \"%s\"", sub);
    strncat(jsonQuery,subQuery,sizeof(jsonQuery));
  }
  if(exp != NULL){
    snprintf(subQuery,sizeof(subQuery),", \"message\" : {\"$regex\": \"%s\" }", exp);
    strncat(jsonQuery,subQuery,sizeof(jsonQuery));
  }
  strncat(jsonQuery,"}",sizeof(jsonQuery));
  
  /*
    now execute the query
  */
  collection = mongoc_client_get_collection(client,instLocal,"log");
  if(collection == NULL){
    snprintf(lastError,sizeof(lastError),"Failed to find log for %s", instLocal);
    status = 1;
    goto cleanup;
  }
  fprintf(stdout,"The query: %s\n", jsonQuery);
  query = bson_new_from_json((const uint8_t *)jsonQuery,strlen(jsonQuery), &err);
  if(query == NULL){
    snprintf(lastError,sizeof(lastError),"Failed to parse query with %s", err.message);
    status = 1;
    goto cleanup;
  }

  cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL);  
  while (mongoc_cursor_next (cursor, &doc)) {
    func(doc,userData);
  }

 cleanup:
  if(sub != NULL){
    free(sub);
  }
  if(exp != NULL){
    free(exp);
  }
  if(query != NULL){
    bson_destroy(query);
  }
  if(cursor != NULL){
    /* mongoc_cursor_destroy(cursor); */
  }
  if(collection != NULL){
    mongoc_collection_destroy(collection);
  }

  return status;
}
