#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "auth.h"
#include "list.h"
#include "util.h"

#define h1(msg, ...)     header(1,msg,__VA_ARGS__)
#define err(n, msg) h1("Error %d: %s", n, msg)

int logfd;

static void header(int n, char* msg, ...) {
  char buf[MAXLEN_HEADER];
  va_list ap;

  memset(buf, 0, MAXLEN_HEADER);

  va_start(ap, msg);
  vsnprintf(buf, MAXLEN_HEADER, msg, ap);
  va_end(ap);

  printf("<h%d>%s</h%d>", n, buf, n);
}

static int handle_error(int err) {
  switch (err) {
  case 400: err(400, "Bad request");   break;
  case 403: err(403, "Forbidden");     break;
  case 500: err(500, "Interal error"); break;
  default:  err(err, "Unknown");       break;
  }
  return err;
}

static char* handle_get()  { return getenv("QUERY_STRING"); }

static char* handle_post() {
  char* buf = malloc(MAXLEN_POST); memset(buf, 0, MAXLEN_POST);
  char* l = getenv("INPUT_LENGTH");
  if (!l) { return NULL; }
  int len = atol(l);
  if (!len) { return NULL; }
  //When MAXLEN_POST needs to be raised, replace code below with loop to read data in chunks
  len = min(len, (MAXLEN_POST-1));
  fread(buf, len, 1, stdin);
  return buf;
}

static void parse_request(char* request, LIST* list) {
  if (!strchr(request, '&')) { //Just one pair
    listQueue(list, request);
    return;
  }
  for (char* pair = strtok(request, "&"); pair; pair = strtok(NULL, "&")) {
    listQueue(list, (void*)pair);
  }
}

static char* getkey(char* pair) {
  char* k = malloc(MAXLEN_KEY);
  memset(k, 0, MAXLEN_KEY);
  int l = 0;

  while (l < (MAXLEN_KEY-1) && pair[l] != '=') {
    *k = pair[l];
    k++; l++;
  }
  return (k-l);
}

static char* getval(char* pair) {
  char* v = strndup(pair, MAXLEN_REQUEST);
  while (v && (*v != '=')) { v++; }
  return v;
}

static char* find_request(char* method) {
  if (!strncmp(method, "GET", 3))  { return handle_get(); }
  if (!strncmp(method, "POST", 3)) { return handle_post(); }
  return NULL;
}

int main() {
  init_prng();
  logfd = open("/var/www/log/log.txt", O_APPEND|O_CREAT|O_DSYNC);
  if (logfd == -1) { return handle_error(500); }
  
  printf("Content-type: text/html\n\n");
  printf("<html><body>");
  
  char* request; char* pair; char* key; char* val;
  char* method     = getenv("REQUEST_METHOD");
  authdb* db       = new_authdb("/var/www/data/test.db");
  if (!db) { return handle_error(500); }
  LIST* args       = newList();

  if (!method) { return handle_error(400); }
  request = find_request(method); 
  if (!request) { return handle_error(400); }
  logs("Full request:%s", request);
  parse_request(request,args);

  LIST_ITERATOR* i = newListIterator(args);
  ITERATE_LIST(pair, i) {
    key = getkey(pair);
    val = getval(pair);
    logs("%s=%s", key, val);
  } deleteListIterator(i);

  if (register_user(db, "admin", "password", "admin")) { logs("main: register_user failed"); }
  else if (user_addperm(db, "admin", "site.login"))    { logs("main: user_addperm failed"); }
  else if (user_has_perm(db, "admin", "site.login"))   { logs("main: user_has_perm failed"); }
  else if (user_setpass(db, "admin", "hello"))         { logs("main: user_setpass failed"); }
  else if (user_validate(db, "admin", "hello"))        { logs("main: user_validate failed"); }
  
  //Below will later be moved into a view system
  printf("Hello, world! DB @ [%p]<br/>", db);
  printf("Request method: %s<br/>", method);
  printf("</body></html>");

  close(logfd);
  return 0;
}

