#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <time.h>
#include <sqlite3.h>
#include <strings.h>
//~ #include "json.h"
#include "jsmn.h"

//~ #define MAXLINE 4096
#define MAXLINE 5000
static char logline[MAXLINE]="";
static char current_time[21]="";
static time_t epoch_time=-1;
static unsigned short PORT = 8080;
static int socket_fd;
static int st_log_to_file=0;
static FILE *file_fd=NULL;

#define LATEST 500
#define MAXDEVEUI 100
static char deveui[MAXDEVEUI][17];
static unsigned short deveui_count=0;

sqlite3 *db;
const char sqlite3_name[]="http-okay.db";
static int rc;
char *errMsg = 0;
const char *sql_table_cnx = "CREATE TABLE IF NOT EXISTS cnx("
                                 "id INTEGER PRIMARY KEY AUTOINCREMENT,"
                                 "ip CHAR(16),"
                                 "port,"
                                 "date CHAR(21));";
const char *sql_table_content = "CREATE TABLE IF NOT EXISTS content("
                                 "id INTEGER PRIMARY KEY AUTOINCREMENT,"
                                 "date CHAR(21),"
                                 "epoch INTEGER,"
                                 "method CHAR(16),"
                                 "host CHAR(256),"
                                 "path CHAR(2048),"
                                 "version CHAR(16),"
                                 "user_agent CHAR(256),"
                                 "content CHAR(4096));";
                                 
const char sql_table_name[] = "content";
const char sql_query_format[] = "SELECT date,content FROM %s ORDER BY id DESC LIMIT %d;";

const char html_output[]="index.2.html";
#define APPNAME "ApplicationServer"
const char html_header[]="<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
"<link rel=\"icon\" type=\"image/png\" href=\"favicon.png\" sizes=\"32x32\">\n"
"<title>"APPNAME"</title>\n"
"<style>\n"
"html{font-family: \"Lucida Sans\", sans-serif;}\n"
"h1,h2{background-color:#3f87a6;color: #fff;}\n"
"table {margin: 0 auto;text-align: center;border-collapse: collapse;border: 1px solid #d4d4d4;height: 500px;}\n"
"tr:nth-child(even) {background: #d4d4d4;}\n"
"th{background-color: #3f87a6;color: #fff;font-weight: bold;}\n"
"th,td{padding: 1px 4px;font-size: 0.8em;}\n"
"th {border-bottom: 1px solid #d4d4d4;}\n"
".sf7 {background-color: #15db00;font-size: 0.7em}\n"
".sf8 {background-color: #408000;font-size: 0.7em}\n"
".sf9 {background-color: #52ad00;font-size: 0.7em}\n"
".sf10 {background-color: #7a8500;font-size: 0.7em}\n"
".sf11 {background-color: #FF5F1F;color: #fff;font-size: 0.7em}\n"
".sf12 {background-color: #f00;color: #fff;font-size: 0.7em}\n"
".sfna {background-color: #fff;}\n"
"#all-stats {font-size: 1.0em;font-family: monospace; display:inline;}\n"  
"li {display: list-item;margin-left: 1em;list-style-type: none;}\n"
".box {width: 30px;text-align: center;display: inline-block;margin-right: 2px;font-size: 0.9em;}\n"
".tooltippacket {visibility: hidden;width: 700px;word-break: break-all;overflow: hidden;background-color: rgba(255,255,255,1);color: #000;padding: 5px 0;padding-left: 10px;position: absolute;z-index: 1;font-size: 0.9em;}\n"
".arrow-up{width: 0;height: 0;margin-bottom: 0px;border-left: 10px solid transparent;border-right: 10px solid transparent;border-bottom: 10px solid #108000;display: inline-block;}"
".arrow-up-gray{width: 0;height: 0;margin-bottom: 0px;border-left: 10px solid transparent;border-right: 10px solid transparent;border-bottom: 10px solid #ccc;display: inline-block;}"
".arrow-up:hover .tooltippacket,"
".arrow-up-gray:hover .tooltippacket {visibility: visible;}\n"
".arrow-up:hover,"
".arrow-up-gray:hover {background-color: yellow;}\n"
".btn-green {background: #1abc9c; margin-right: 4px;}\n"
"</style>\n"
"</head>\n"
"<body>\n"
"  <h2>"APPNAME"</h2>\n";

const char html_header_2[]="<script>\n"
"function filterOut(classname) {\n"
"  const collection = document.getElementsByClassName(classname);\n"
"  for (let i = 0; i < collection.length; i++) {\n"
"    if(collection[i].style.display == 'none' ) {\n"
"      collection[i].style.display='table-row';\n"
"      document.getElementById(\"f\"+classname).style.color=\"#000\";\n"
"    } else {\n"
"      collection[i].style.display='none';\n"
"      document.getElementById(\"f\"+classname).style.color=\"#faa\";\n"
"    }\n"
"  }\n"
"}\n"
"function clickAll() {\n"
"  document.querySelectorAll('.stat-box').forEach(box => box.click());\n"
"}\n"
"</script>\n"
"</body></html>\n"
"\n";

const char html_stat_start[]="<button class=\"btn-green\" onclick=\"clickAll()\">ToggleAll</button>\n<button class=\"btn-green\" onClick=\"window.location.reload();\">Reload</button>\n"
"<hr />\n"
"<div id=\"all-stats\">\n";
const char html_stat_end[]="</div>\n<hr>\n";

static char *get_rfc3339() {  
  epoch_time = time(NULL);
  struct tm *tm_info;
  tm_info = gmtime(&epoch_time);
  strftime(current_time, 21, "%Y-%m-%dT%H:%M:%SZ", tm_info);
  return current_time;
}


const char* colorSnr(float f) {
  if( f < -7.5) {
    return "sf12";
  } else if( f < -5) {
    return "sf11";
  } else if( f < -2.5) {
    return "sf10";
  } else if( f < 0) {
    return "sf9";
  } else if( f < 2.5) {
    return "sf8";
  } else {
    return "sf7";
  }
}
const char* colorRssi(float f) {
  if( f < -105) {
    return "sf12";
  } else if( f < -95) {
    return "sf11";
  } else if( f < -85) {
    return "sf10";
  } else if( f < 80) {
    return "sf9";
  } else {
    return "sf7";
  }
}

static void *stats_box(char *stats) {
  //~ char stats[4096]="";
  strncat(stats,html_stat_start,sizeof(html_stat_start));
  for(int i =0; i < deveui_count;i++) {
    char temp[256]="";
    sprintf(temp,"<div class=\"stat-box\" id=\"f%s\" onclick=\"filterOut('%s');\" style=\"cursor: pointer;\">%s</div>\n",
      deveui[i],
      deveui[i],
      deveui[i]);
    strcat(stats,temp);
  }
  strncat(stats,html_stat_end,sizeof(html_stat_end));
}

static void usage() {
  printf("Usage: http-okay <PORT> <OUTFILE>\n");
  exit(0);
}

static void mylog() {
  printf("%s",logline);
  fflush(stdout);
  if( 0 != st_log_to_file ) {
    if( NULL != file_fd ) {
      fwrite(logline,1,strlen(logline),file_fd);
      fflush(file_fd);
    }
  }
}

static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
  if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start &&
      strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
    return 0;
  }
  return -1;
}

typedef struct {
    char method[16];
    char path[2048];
    char version[16];
    char user_agent[256];
    char host[256];
    char content[4096];
} http_req_t;

void http_req_parse(const char *request, http_req_t *http_request) {
  char *request_copy = strdup(request);
  char *line = strtok(request_copy, "\r\n");
  if (line) {
    sscanf(line, "%15s %255s %15s", http_request->method, http_request->path, http_request->version);
  }

  bzero(&http_request->content, 4096);
  bzero(&http_request->host, 256);
  bzero(&http_request->user_agent, 256);

  int content_length = 0;
  while (line != NULL && strlen(line) > 0) {
    if (strncmp(line, "User-Agent: ", 12) == 0) {
      sscanf(line + 12, "%255[^\r\n]", http_request->user_agent);
    } else if (strncmp(line, "Host: ", 6) == 0) {
      sscanf(line + 6, "%255[^\r\n]", http_request->host);
    } else if (strncmp(line, "Content-Length: ", 16) == 0) {
      sscanf(line + 16, "%d", &content_length);
    }
    //printf("DEBUG: %s\n", line);
    line = strtok(NULL, "\r\n");
  }

  if( content_length ) {
    strncpy(http_request->content,request+strlen(request)-content_length,content_length);
  }
  free(request_copy);
}

static void signal_int_handler() {
  printf("\nExiting\n");
  close(socket_fd);
  if( NULL != file_fd ) {
    fclose(file_fd);
  }
  exit(0);
}

static int mysqlite3_callback(void *NotUsed, int argc, char **argv, char **azColName) {
  for (int i = 0; i < argc; i++) {
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}

void mysqlite3_create() {
  rc = sqlite3_open(sqlite3_name, &db);
  if(rc) {
    fprintf(stderr,"Can't open database: %s\n", sqlite3_errmsg(db));
  } else {
    snprintf(logline, MAXLINE, "%s : Database %s open success\n",get_rfc3339(), sqlite3_name);
    mylog();
  }
  rc = sqlite3_exec(db, sql_table_cnx, mysqlite3_callback, 0, &errMsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr,"SQL error: %s\n", errMsg);
    sqlite3_free(errMsg);
  } else {
    snprintf(logline, MAXLINE, "%s : Table CNX create success\n", get_rfc3339());
    mylog();
  }
  rc = sqlite3_exec(db, sql_table_content, mysqlite3_callback, 0, &errMsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr,"SQL error: %s\n", errMsg);
    sqlite3_free(errMsg);
  } else {
    snprintf(logline, MAXLINE, "%s : Table CONTENT create success\n", get_rfc3339());
    mylog();
  }
}

void mysqlite3_begin() {
  const char *sqlBeginTransaction = "BEGIN TRANSACTION;";
  rc = sqlite3_exec(db, sqlBeginTransaction, mysqlite3_callback, 0, &errMsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "SQL error (Begin Transaction): %s\n", errMsg);
    sqlite3_free(errMsg);
  }
}
void mysqlite3_commit() {
  const char *sqlCommit = "COMMIT;";
  rc = sqlite3_exec(db, sqlCommit, mysqlite3_callback, 0, &errMsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "SQL error (Commit): %s\n", errMsg);
    sqlite3_free(errMsg);
  }
}


void mysqlite3_content_insert(http_req_t *http_req) {
  mysqlite3_begin();
  const char *sql_insert_content_templ = "INSERT INTO content (date, epoch, host, method, path, version, user_agent, content) "
                                "VALUES ('%s', %d, '%s', '%s', '%s', '%s', '%s', '%s');";
  char sql_insert_content_values[4096];
  bzero(sql_insert_content_values, 4096);

  strcat(sql_insert_content_values,"INSERT INTO content (date, epoch, host, method, path, version, user_agent, content) VALUES (");
  char sql_str_templ[16]="'%s',";
  char sql_int_templ[16]="%d,";
  char sql_str_templ_last[16]="'%s');";
  char sql_str_temp[4095];
  sprintf(sql_str_temp,sql_str_templ,get_rfc3339());
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_int_templ,epoch_time);
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_str_templ,http_req->host);
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_str_templ,http_req->method);
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_str_templ,http_req->path);
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_str_templ,http_req->version);
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_str_templ,http_req->user_agent);
  strcat(sql_insert_content_values,sql_str_temp);

  sprintf(sql_str_temp,sql_str_templ_last,http_req->content);
  strcat(sql_insert_content_values,sql_str_temp);

  rc = sqlite3_exec(db, sql_insert_content_values, mysqlite3_callback, 0, &errMsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "SQL error (Insert): %s\n", errMsg);
    sqlite3_free(errMsg);
  }
  mysqlite3_commit();
}

void mysqlite3_cnx_insert(char *date,char *ip,unsigned short port) {
  mysqlite3_begin();
  const char *sql_insert_cnx_templ = "INSERT INTO cnx (ip, port, date) "
                                "VALUES ('%s', %d, '%s'); ";
  char sql_insert_cnx_values[256]="";
  snprintf(sql_insert_cnx_values,256,sql_insert_cnx_templ,ip,port,date);

  rc = sqlite3_exec(db, sql_insert_cnx_values, mysqlite3_callback, 0, &errMsg);
  if (rc != SQLITE_OK) {
    fprintf(stderr, "SQL error (Insert): %s\n", errMsg);
    sqlite3_free(errMsg);
  }
  mysqlite3_commit();
}

void mysqlite3_query(int limit) {
  char sql_query[256];
  snprintf(sql_query, sizeof(sql_query), sql_query_format, sql_table_name, limit);
  snprintf(logline, MAXLINE, "%s : Extracting latest %d records\n", get_rfc3339(), limit);
  mylog();
  
  sqlite3_stmt *stmt;
  if (sqlite3_prepare_v2(db, sql_query, -1, &stmt, NULL) == SQLITE_OK) {
  	int count=0;
  	
  	FILE *fd = fopen(html_output,"wt");
  	fwrite(html_header,sizeof(html_header)-1,1,fd);
  	fwrite(html_header_2,sizeof(html_header_2)-1,1,fd);
  	char generated_date[64]="";
    //memset(generated_date, 0x0, 64 );
    bzero(generated_date,64);
  	sprintf(generated_date,"\n<p>Generated at %s</p>\n", get_rfc3339());
  	fwrite(generated_date,strlen(generated_date),1,fd);
  	//~ fwrite(html_stat_start,sizeof(html_stat_start),1,fd);
  	//~ fwrite(html_stat_end,sizeof(html_stat_end),1,fd);

  	
  	char html_table[4096]="";
    memset(html_table, 0x0, 4096);
    snprintf(html_table, 4096, "<table>\n<tr>\n<th>Time</th>\n<th>DevEUI</th>\n<th>FPort</th>\n<th>FCntUp</th>\n<th>FCntDn</th>\n<th>Payload</th>\n<th>SF</th>\n<th>RSSI</th>\n<th>SNR</th>\n<th>LC</th>\n<th>LRRID</th>\n<th>Late</th>\n<th>Count</th>\n<th>MAC</th>\n</tr>\n");
  	fwrite(html_table,strlen(html_table),1,fd);
  	
    while (sqlite3_step(stmt) == SQLITE_ROW) {
      const unsigned char *rec_content = sqlite3_column_text(stmt, 1);
      const unsigned char *rec_date = sqlite3_column_text(stmt, 0);
      
      int r;
      jsmn_parser p;
      jsmn_init(&p);
      jsmntok_t t[256];
      r = jsmn_parse(&p, rec_content, strlen(rec_content), t, sizeof(t) / sizeof(t[0]));
      if (r < 0) {
        snprintf(logline, MAXLINE, "%s : Failed to parse JSON: %d\n", get_rfc3339(), r);
        mylog();
      }
      if (r < 1 || t[0].type != JSMN_OBJECT) {
        snprintf(logline, MAXLINE, "%s :   Missing object\n", get_rfc3339());
        mylog();
      } else {
        char *p_Time= NULL;
        char *p_DevEUI= NULL;
        char *p_FPort= NULL;
        char *p_FCntUp= NULL;
        char *p_FCntDn= NULL;
        char *p_payload_hex= NULL;
        char *p_SpFact= NULL;
        char *p_LrrRSSI= NULL;
        float f_LrrRSSI= -199;
        char *p_LrrSNR= NULL;
        float f_LrrSNR= 99;
        char *p_Channel= NULL;
        char *p_Lrrid= NULL;
        char *p_Late= NULL;
        unsigned short i_Late=0;
        char *p_DevLrrCnt= NULL;
        char *p_rawMacCommands= NULL;
        char empty[]="";
        int free_p_rawMacCommands=0;
		for (int i = 1; i < r; i++) {
          if (jsoneq(rec_content, &t[i], "Time") == 0) {
			p_Time=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_Time: %s\n", p_Time);
            i++;
          } else if (jsoneq(rec_content, &t[i], "DevEUI") == 0) {
			p_DevEUI=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_DevEUI: %s\n", p_DevEUI);
            i++;
          } else if (jsoneq(rec_content, &t[i], "FPort") == 0) {
			p_FPort=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_FPort: %s\n", p_FPort);
            i++;
          } else if (jsoneq(rec_content, &t[i], "FCntUp") == 0) {
			p_FCntUp=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_FCntUp: %s\n", p_FCntUp);
            i++;
          } else if (jsoneq(rec_content, &t[i], "FCntDn") == 0) {
			p_FCntDn=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_FCntDn: %s\n", p_FCntDn);
            i++;
          } else if (jsoneq(rec_content, &t[i], "payload_hex") == 0) {
			p_payload_hex=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_payload_hex: %s\n", p_payload_hex);
            i++;
          } else if (jsoneq(rec_content, &t[i], "SpFact") == 0) {
			p_SpFact=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_SpFact: %s\n", p_SpFact);
            i++;
          } else if (jsoneq(rec_content, &t[i], "LrrRSSI") == 0) {
			p_LrrRSSI=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
			f_LrrRSSI=(float)strtod(p_LrrRSSI,NULL);
            //~ printf("     p_LrrRSSI: %s\n", p_LrrRSSI);
            i++;
          } else if (jsoneq(rec_content, &t[i], "LrrSNR") == 0) {
			p_LrrSNR=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_LrrSNR: %s\n", p_LrrSNR);
            f_LrrSNR=(float)strtod(p_LrrSNR,NULL);
            i++;
          } else if (jsoneq(rec_content, &t[i], "Channel") == 0) {
			p_Channel=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_Channel: %s\n", p_Channel);
            i++;
          } else if (jsoneq(rec_content, &t[i], "Lrrid") == 0) {
			p_Lrrid=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_Lrrid: %s\n", p_Lrrid);
            i++;
          } else if (jsoneq(rec_content, &t[i], "Late") == 0) {
			p_Late=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_Late: %s\n", p_Late);
            i_Late=atoi(p_Late);
            //~ printf("     i_Late: %d\n", i_Late);
            i++;
          } else if (jsoneq(rec_content, &t[i], "DevLrrCnt") == 0) {
			p_DevLrrCnt=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
            //~ printf("     p_DevLrrCnt: %s\n", p_DevLrrCnt);
            i++;
          } else if (jsoneq(rec_content, &t[i], "rawMacCommands") == 0) {
            //~ printf("     p_rawMacCommands: %s\n", p_rawMacCommands);
            if( 0 == t[i + 1].end - t[i + 1].start ) {
			  p_rawMacCommands=(char *)empty;
			} else {
			  p_rawMacCommands=strndup(rec_content + t[i + 1].start,t[i + 1].end - t[i + 1].start);
			  free_p_rawMacCommands=1;
			}
            i++;
          }
	    }
	    
	    char uplink_type[32]="arrow-up";
        if(i_Late) {
          strcpy(uplink_type,"arrow-up-gray");
	    }
	    
	    if(!p_rawMacCommands) {
          p_rawMacCommands=empty;
		}	    
		
		unsigned short found=0;
		for(int j = 0; j<deveui_count;j++) {
          if(0 == strcmp(deveui[j],p_DevEUI)) {
            found=1;
            break;
		  }
		}
		if( 0 == found ) {
          printf("Adding %s\n", p_DevEUI);
          strcpy(deveui[deveui_count],p_DevEUI);
          deveui_count++;
		}
		
	    char table_line[4096]="";
      memset(table_line, 0x0, 4096 );
	    sprintf(table_line, "<tr class=\"%s\"><td><div class=\"%s\"><span class=\"tooltippacket\">%s</span></div>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td class=\"sf%s\">%s</td><td class=\"%s\">%s</td><td class=\"%s\">%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n",
	      p_DevEUI,
	      uplink_type,
	      rec_content,
	      p_Time,
	      p_DevEUI,
	      p_FPort,
	      p_FCntUp,
	      p_FCntDn,
	      p_payload_hex,
	      p_SpFact,
	      p_SpFact,
	      colorRssi(f_LrrRSSI),
	      p_LrrRSSI,
	      colorSnr(f_LrrSNR),
	      p_LrrSNR,
	      p_Channel,
	      p_Lrrid,
	      p_Late,
	      p_DevLrrCnt,
	      p_rawMacCommands);
	    fwrite(table_line,strlen(table_line),1,fd);
	    if(p_Time) free(p_Time);
	    if(p_DevEUI) free(p_DevEUI);
	    if(p_FPort) free(p_FPort);
	    if(p_FCntUp) free(p_FCntUp);
	    if(p_FCntDn) free(p_FCntDn);
	    if(p_payload_hex) free(p_payload_hex);
		if(p_SpFact) free(p_SpFact);
        if(p_LrrRSSI) free(p_LrrRSSI);
        if(p_LrrSNR) free(p_LrrSNR);
        if(p_Channel) free(p_Channel);
        if(p_Lrrid) free(p_Lrrid);
        if(p_Late) free(p_Late);
        if(p_DevLrrCnt) free(p_DevLrrCnt);
        if(p_rawMacCommands && free_p_rawMacCommands ) free(p_rawMacCommands);
	  }
            
      //snprintf(logline, MAXLINE, "%s : Record%d: %s\n", get_rfc3339(), count, rec_date);
      //mylog();
      count++;
    }
    fwrite("</table>\n",strlen("</table>\n"),1,fd);
    char all_stats[4096];
    bzero(all_stats,4096);
    stats_box(all_stats);
    fwrite(all_stats,strlen(all_stats),1,fd);
    fclose(fd);
    sqlite3_finalize(stmt);
  } else {
    fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
  }
}

void response_to_client(int connection_fd) {
  char buf[4096] = {0};
  while (1) {
    const int read_size = read(connection_fd,&buf, sizeof(buf));
    if (read_size == -1) {
      if (errno == EINTR) {
        continue;
      }
      printf("Unable to read socket : %s\n", strerror(errno));
      exit(1);
    }
    if (read_size == 0) {
      return;
    }
    break;
  }
  snprintf(logline, MAXLINE, "%s : %s\n", get_rfc3339(), buf);
  mylog();
  snprintf(logline, MAXLINE, "%s : ------------\n",get_rfc3339());
  mylog();

  http_req_t http_request;
  http_req_parse(buf,&http_request);
  /*
  printf("  Method: %s\n", http_request.method);
  printf("  Host: %s\n", http_request.host);
  printf("  Path: %s\n", http_request.path);
  printf("  Version: %s\n", http_request.version);
  printf("  User-Agent: %s\n", http_request.user_agent);
  printf("  Content: %s\n", http_request.content);
  */
  mysqlite3_content_insert(&http_request);

  const char msg[] = "HTTP/1.1 200 OK\r\n"
    "Content-Type: text/plain\r\n"
    "Content-Length: 2\r\n\r\n"
    "OK\r\n";


  int offset = 0;
  while (offset < strlen(msg)) {
    while (1) {
      int write_response = write(connection_fd, msg+offset, strlen(msg+offset));
      if (write_response == -1) {
        if (errno == EINTR) {
          continue;
        }

        printf("Unable to write socket : %s\n", strerror(errno));
        exit(1);
      }
      offset += write_response;
      break;
    }
  }
}


void main(int argc,char **argv) {
  signal(SIGINT, signal_int_handler);

  if(argc > 1) {
    if( 0 == strcmp(argv[1], "help")) {
      usage();
    } else if( 0 == strcmp(argv[1], "--help")) {
      usage();
    } 
    if( argc > 2 ) {
      st_log_to_file=1;
      file_fd=fopen(argv[2],"at+");
    }
  }
  
  for(int i = 0; i < MAXDEVEUI; i++ ){
    strcpy(deveui[i],"-1");
  }

  //SQLITE
  mysqlite3_create();

  mysqlite3_query(LATEST);

  socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (socket_fd == -1) {
    fprintf(stderr,"Unable to create socket file descriptor: %s\n", strerror(errno));
    exit(1);
  }

  struct sockaddr_in server_address, client_address;
  bzero(&server_address, sizeof(server_address));
  server_address.sin_family = AF_INET;
  if ( argc > 1 ) {
    PORT = atoi(argv[1]);
  }
  server_address.sin_port = htons(PORT);
  snprintf(logline, MAXLINE, "%s : Listening on %d\n", get_rfc3339(), PORT);
  mylog();
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);

  const int reuse = 1;
  const int reuse_addr_response = setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
  if (reuse_addr_response == -1) {
    fprintf(stderr, "Unable to set SO_REUSEADDR to socket\n");
    exit(1);
  }

  const int bind_response = bind(socket_fd, (struct sockaddr *)&server_address, sizeof(server_address));
  if (bind_response == -1) {
    fprintf(stderr, "Unable to bind the socket to file descriptor : %s\n", strerror(errno));
    exit(1);
  }

  const int listen_response = listen(socket_fd, 5);
  if (listen_response != 0) {
    fprintf(stderr, "Unable to listen to port %d: %s", PORT, strerror(errno));
    exit(1);
  }



  while (1) {
    unsigned int client_socket_len = sizeof(client_address);
    const int connection_fd = accept(socket_fd, (struct sockaddr*)&client_address, &client_socket_len);
    if (connection_fd == -1) {
      fprintf(stderr, "Unable to accept incoming connection : %s\n", strerror(errno));
    }
    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &client_address.sin_addr, client_ip, INET_ADDRSTRLEN);
    snprintf(logline, MAXLINE, "%s : Connection from %s:%d\n", get_rfc3339(), client_ip, ntohs(client_address.sin_port));
    mylog();

    mysqlite3_cnx_insert(get_rfc3339(),client_ip,ntohs(client_address.sin_port));
    response_to_client(connection_fd);
    close(connection_fd);
    mysqlite3_query(LATEST);
  }
}

