#include "http.h"
#include "httprequest.h"
#include "httpresponse.h"
#include "callbackqueue.h"
#include "socket.h"
#include "utils.h"
#include "thread.h"
#include <stdlib.h>
#include <string.h>

#include <algorithm>
#include <iostream>
#include <sstream>
#include <memory>


// TODO: Streaming response body (with chunked transfer encoding)
// TODO: Fast/easy use of files as response body

void on_request(uv_stream_t* handle, int status) {
  ASSERT_BACKGROUND_THREAD()
  if (status) {
    err_printf("connection error: %s\n", uv_strerror(status));
    return;
  }

  // Copy the shared_ptr
  std::shared_ptr<Socket> pSocket(*(std::shared_ptr<Socket>*)handle->data);
  CallbackQueue* bg_queue = pSocket->background_queue;

  // Freed by HttpRequest itself when close() is called, which
  // can occur on EOF, error, or when the Socket is destroyed
  std::shared_ptr<HttpRequest> req = createHttpRequest(
    handle->loop, pSocket->pWebApplication, pSocket, bg_queue
  );

  int r = uv_accept(handle, req->handle());
  if (r) {
    err_printf("accept: %s\n", uv_strerror(r));
    return;
  }

  req->handleRequest();

}

uv_stream_t* createPipeServer(
  uv_loop_t* pLoop,
  const std::string& name,
  int mask,
  std::shared_ptr<WebApplication> pWebApplication,
  bool quiet,
  CallbackQueue* background_queue
) {
  ASSERT_BACKGROUND_THREAD()

  // We own pWebApplication. It will be destroyed by the socket but if in
  // the future we have failure cases that stop execution before we get
  // that far, we MUST delete pWebApplication ourselves.

  std::shared_ptr<Socket> pSocket = std::make_shared<Socket>(
    pWebApplication, background_queue
  );

  // TODO: Handle error
  uv_pipe_init(pLoop, &pSocket->handle.pipe, 0);
  pSocket->handle.isTcp = false;
  // data is a pointer to the shared_ptr. This is necessary because the
  // uv_stream_t.data field is a void*.
  pSocket->handle.stream.data = new std::shared_ptr<Socket>(pSocket);

  mode_t oldMask = 0;
  if (mask >= 0)
    oldMask = umask(mask);
  int r = uv_pipe_bind(&pSocket->handle.pipe, name.c_str());
  if (mask >= 0)
    umask(oldMask);

  if (r) {
    if (!quiet)
      err_printf("createPipeServer: %s\n", uv_strerror(r));
    // It's important that close() is explicitly called, so that the uv_pipe_t is cleaned up
    pSocket->close();
    return NULL;
  }
  r = uv_listen((uv_stream_t*)&pSocket->handle.stream, 128, &on_request);
  if (r) {
    if (!quiet)
      err_printf("createPipeServer: %s\n", uv_strerror(r));
    // It's important that close() is explicitly called, so that the uv_pipe_t is cleaned up
    pSocket->close();
    return NULL;
  }

  return &pSocket->handle.stream;
}

// A wrapper for createPipeServer. The main thread schedules this to run on
// the background thread, then waits for this to finish, using a barrier.
void createPipeServerSync(
  uv_loop_t* loop,
  const std::string& name,
  int mask,
  std::shared_ptr<WebApplication> pWebApplication,
  bool quiet,
  CallbackQueue* background_queue,
  uv_stream_t** pServer,
  std::shared_ptr<Barrier> blocker
) {
  ASSERT_BACKGROUND_THREAD()

  *pServer = createPipeServer(loop, name, mask, pWebApplication, quiet, background_queue);

  // Tell the main thread that the server is ready
  blocker->wait();
}

uv_stream_t* createTcpServer(
  uv_loop_t* pLoop,
  const std::string& host,
  int port,
  std::shared_ptr<WebApplication> pWebApplication,
  bool quiet,
  CallbackQueue* background_queue
) {
  ASSERT_BACKGROUND_THREAD()

  // We own pWebApplication. It will be destroyed by the socket but if in
  // the future we have failure cases that stop execution before we get
  // that far, we MUST delete pWebApplication ourselves.

  std::shared_ptr<Socket> pSocket = std::make_shared<Socket>(
    pWebApplication, background_queue
  );

  // TODO: Handle error
  uv_tcp_init(pLoop, &pSocket->handle.tcp);
  pSocket->handle.isTcp = true;
  // data is a pointer to the shared_ptr. This is necessary because the
  // uv_stream_t.data field is a void*.
  pSocket->handle.stream.data = new std::shared_ptr<Socket>(pSocket);

  int r;
  // Lifetime of these needs to encompass use of pAddress in uv_tcp_bind()
  struct sockaddr_in6 addr6;
  struct sockaddr_in  addr4;
  sockaddr* pAddress;
  int family = ip_family(host);
  if (family == AF_INET6) {
    r = uv_ip6_addr(host.c_str(), port, &addr6);
    pAddress = reinterpret_cast<sockaddr*>(&addr6);
  } else if (family == AF_INET){
    r = uv_ip4_addr(host.c_str(), port, &addr4);
    pAddress = reinterpret_cast<sockaddr*>(&addr4);
  } else {
    r = 1;
    if (!quiet)
      err_printf("%s is not a valid IPv4 or IPv6 address.\n", host.c_str());
  }

  if (r) {
    if (!quiet)
      err_printf("createTcpServer: %s\n", uv_strerror(r));
    // It's important that close() is explicitly called, so that the uv_tcp_t is cleaned up
    pSocket->close();
    return NULL;
  }

  r = uv_tcp_bind(&pSocket->handle.tcp, pAddress, 0);

  if (r) {
    if (!quiet)
      err_printf("createTcpServer: %s\n", uv_strerror(r));
    // It's important that close() is explicitly called, so that the uv_tcp_t is cleaned up
    pSocket->close();
    return NULL;
  }
  r = uv_listen((uv_stream_t*)&pSocket->handle.stream, 128, &on_request);
  if (r) {
    if (!quiet)
      err_printf("createTcpServer: %s\n", uv_strerror(r));
    // It's important that close() is explicitly called, so that the uv_tcp_t is cleaned up
    pSocket->close();
    return NULL;
  }

  return &pSocket->handle.stream;
}

// A wrapper for createTcpServer. The main thread schedules this to run on the
// background thread, then waits for this to finish, using a barrier.
void createTcpServerSync(
  uv_loop_t* pLoop,
  const std::string& host,
  int port,
  std::shared_ptr<WebApplication> pWebApplication,
  bool quiet,
  CallbackQueue* background_queue,
  uv_stream_t** pServer,
  std::shared_ptr<Barrier> blocker
) {
  ASSERT_BACKGROUND_THREAD()

  *pServer = createTcpServer(pLoop, host, port, pWebApplication, quiet, background_queue);

  // Tell the main thread that the server is ready
  blocker->wait();
}


void freeServer(uv_stream_t* pHandle) {
  ASSERT_BACKGROUND_THREAD()
  // TODO: Check if server is still running?
  std::shared_ptr<Socket>* ppSocket = (std::shared_ptr<Socket>*)pHandle->data;
  (*ppSocket)->close();
  // ppSocket gets deleted in a callback in close()
}
