// 
// gcc mongoose.c diuhttp.c -o diuhttp
//
#include "mongoose.h"

#include <sys/time.h>

#define _SLEN 1024

static const char *s_http_port = "60535";
static struct mg_serve_http_opts s_http_server_opts;

static int is_websocket(const struct mg_connection *nc) {
  return nc->flags & MG_F_IS_WEBSOCKET;
}

long long int _get_usec_time() {
  struct timeval tv;
  gettimeofday(&tv,NULL);
  return (long long int)tv.tv_usec + (1000000 * (long long int)tv.tv_sec);
}


//
// ui wants status information
//
static void handle_status_input(struct mg_connection *nc, struct http_message *hm) {
  char buf[_SLEN];
  char msg[][_SLEN] = {
    "fail error",
    "ok .",
  };

  //DEBUG sample message
  snprintf(buf, _SLEN, "ok msg=status\nroute=%s|trip=%s|stop=%s|gps=%i|tunnel=%i|date=%s|eqiupno=%s|nmsg=%s|last_token=%s|config=%s|firmware=%s|imei=%s|imsi=%s|eth0=%s",
      "9900", "1", "0",
      1, 1,
      "2019-08-13 09;23:00",
      "9999", "0",
      "",
      "20190730v403 2019-11-05 02:36:43",
      "1.11 2019-07-30 16:33:08",
      "356136074279052",
      "310260877138191",
      "00:80:66:10:E8:8A");

  /*
  printf(">> status:\nbuf(%i):\n%s\n", (int)strlen(buf), buf);

  //snprintf(buf, _SLEN, "ok xxx=foo bar;yyy=baz qux;...");

  printf(">> status\n");
  */

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(buf), buf);

}

// driver logout
//
static void handle_logout_input(struct mg_connection *nc, struct http_message *hm) {
  char msg[][_SLEN] = {
    "fail error",
    "ok .",
  };

  printf(">> logout\n");

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(msg[1]), msg[1]);

}

// (manual) next stop pressed
//
static void handle_nextstop_input(struct mg_connection *nc, struct http_message *hm) {
  char msg[][_SLEN] = {
    "fail error",
    "ok .",
  };

  printf(">> nextstop\n");

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(msg[1]), msg[1]);

}

// (manual) previous stop pressed
//
static void handle_prevstop_input(struct mg_connection *nc, struct http_message *hm) {
  char msg[][_SLEN] = {
    "fail error",
    "ok .",
  };

  printf(">> prevstop\n");

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(msg[1]), msg[1]);

}

// generic fare input
//
static void handle_fare_input(struct mg_connection *nc, struct http_message *hm) {
  int ret;
  char s_fare[_SLEN], s_count[_SLEN];
  char msg[][_SLEN] = {
    "fail error",
    "ok .",
  };

  ret = mg_get_http_var(&(hm->body), "fare", s_fare, _SLEN);
  if (ret<=0) { mg_http_send_error(nc, 404, NULL); return; }

  printf("got fare %s\n", s_fare);

  ret = mg_get_http_var(&(hm->body), "count", s_count, _SLEN);
  if (ret<=0) { mg_http_send_error(nc, 404, NULL); return; }

  printf("got count %s\n", s_count);

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(msg[1]), msg[1]);

}

// driver paddle (route) input
//
static void handle_paddle_input(struct mg_connection *nc, struct http_message *hm) {
  int ret;
  char s_paddle[_SLEN];

  char msg[][_SLEN] = {
    "fail paddle_unknown",
    "ok paddle",
  };

  ret = mg_get_http_var(&(hm->body), "paddle", s_paddle, _SLEN);
  if (ret<=0) { mg_http_send_error(nc, 404, NULL); return; }

  //DEBUG
  printf("#got paddle %s\n", s_paddle);

  //DEBUG
  if (strncmp(s_paddle,"9900", strlen("9900"))!=0) {
    mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
        (unsigned long)strlen(msg[0]), msg[0]);
    return;
  }

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(msg[1]), msg[1]);

}

// driver login
//
static void handle_driver_login(struct mg_connection *nc, struct http_message *hm) {
  int ret;
  char s_driver[_SLEN], s_pin[_SLEN];

  char msg[][_SLEN] = {
    "fail driver_not_found",
    "fail pin_invalid",
    "ok driver",
    "ok admin",
  };

  ret = mg_get_http_var(&(hm->body), "driver", s_driver, _SLEN);
  if (ret<=0) { mg_http_send_error(nc, 404, NULL); return; }

  ret = mg_get_http_var(&(hm->body), "pin", s_pin, _SLEN);
  if (ret<=0) { mg_http_send_error(nc, 404, NULL); return; }

  //DEBUG
  printf("#got %s %s\n", s_driver, s_pin);

  //DEBUG
  if (strncmp(s_driver,"81000", strlen("81000"))!=0) {
    mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
        (unsigned long)strlen(msg[0]), msg[0]);

    return;
  }

  if (strncmp(s_pin,"81000", strlen("81000"))!=0) {
    mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
        (unsigned long)strlen(msg[1]), msg[1]);
    return;
  }

  mg_printf(nc, "HTTP/1.1 200 OK\r\nContent-Length: %lu\r\n\r\n%s",
      (unsigned long)strlen(msg[3]), msg[3]);

}

// api point
//
static void handle_req(struct mg_connection *nc, struct http_message *hm) {
  int ret;
  char s_action[_SLEN];
  char _default_msg[_SLEN] = "";
  struct mg_str hdr;

  ret = mg_get_http_var(&(hm->body), "action", s_action, _SLEN);

  if (ret==0) {
    printf(">>> %p\n", s_action);
    mg_http_send_error(nc, 404, NULL);
    return;
  }

  printf("#req: action: (%s)\n", s_action);

  if (strncmp(s_action, "driverlogin", strlen("driverlogin"))==0) {
    handle_driver_login(nc, hm);
  }

  else if (strncmp(s_action, "paddleinput", strlen("paddleinput"))==0) {
    handle_paddle_input(nc, hm);
  }

  else if (strncmp(s_action, "prevstop", strlen("prevstop"))==0) {
    handle_prevstop_input(nc, hm);
  }

  else if (strncmp(s_action, "nextstop", strlen("nextstop"))==0) {
    handle_nextstop_input(nc, hm);
  }

  else if (strncmp(s_action, "status", strlen("status"))==0) {
    handle_status_input(nc, hm);
  }

  else if (strncmp(s_action, "logout", strlen("logout"))==0) {
    handle_logout_input(nc, hm);
  }

  else if (strncmp(s_action, "fare", strlen("fare"))==0) {
    handle_fare_input(nc, hm);
  }

  else {
    mg_http_send_error(nc, 404, NULL);
  }

}

static void process_ws_message(struct websocket_message *ws_msg) {
  int i;
  char *data;

  data = (char *)(ws_msg->data);
  printf("#ws, got(%i)\n:", (int)(ws_msg->size));
  for (i=0; i<ws_msg->size; i++) {
    printf("%c", data[i]);
  }
  printf("\n");
}

static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  struct http_message *hm = (struct http_message *) ev_data;

  int debug_print = 0;
  int i;

  char buf[1024];

  if (debug_print) {
    mg_sock_addr_to_str(&(nc->sa), buf, 1023, MG_SOCK_STRINGIFY_IP);
    printf("%s\n", buf);
  }

  switch (ev){

    case MG_EV_HTTP_REQUEST:
      if (mg_vcmp(&hm->uri, "/req")==0) {
        handle_req(nc, (struct http_message *)ev_data);
      }
      else {
        mg_serve_http(nc, (struct http_message *) ev_data, s_http_server_opts);
      }
      break;

    case MG_EV_WEBSOCKET_HANDSHAKE_DONE:
      printf("ws handshake done\n");
      break;
    case MG_EV_WEBSOCKET_FRAME:
      process_ws_message((struct websocket_message *)ev_data);
      break;

    case MG_EV_CLOSE:
      if (is_websocket(nc)) {
        printf("ws closed\n");
      }
      break;

    default:
      //printf("? %i\n", ev);
      break;
  }

}

static void foo(struct mg_mgr *mgr) {
  int n;
  char buf[_SLEN];
  struct mg_connection *c;

  for (c=mg_next(mgr, NULL); c ; c = mg_next(mgr, c)) {
    printf("...\n");

    snprintf(buf, _SLEN, "hello");
    n = strlen(buf);
    mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, n);
  }

}

static void send_ws_heartbeat(struct mg_connection *nc) {
  int n;
  char buf[_SLEN];
  struct mg_connection *c;

  for (c=mg_next(nc->mgr, NULL); c ; c = mg_next(nc->mgr, c)) {
    if (c==nc) { continue; }
    if (!is_websocket(c)) { continue; }
    printf("...\n");

    snprintf(buf, _SLEN, "hello");
    n = strlen(buf);
    mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, n);
  }

}

int main(void) {
  struct mg_mgr mgr;
  struct mg_connection *nc;
  struct timeval tv_now, tv_prv;
	long long int _tnow, _tprv;

  mg_mgr_init(&mgr, NULL);
  printf("Starting web server on port %s\n", s_http_port);
  nc = mg_bind(&mgr, s_http_port, ev_handler);
  if (nc == NULL) {
    printf("Failed to create listener\n");
    return 1;
  }

  // Set up HTTP server parameters
  mg_set_protocol_http_websocket(nc);
  s_http_server_opts.document_root = "html";
  s_http_server_opts.enable_directory_listing = "no";

  _tprv = _get_usec_time();

  for (;;) {
    mg_mgr_poll(&mgr, 1000);

    _tnow = _get_usec_time();
    if ((_tnow - _tprv) > 1000) {
      send_ws_heartbeat(nc);
      _tprv = _tnow;
    }
    //foo(&mgr);
  }
  mg_mgr_free(&mgr);

  return 0;
}

