#ifdef HAVE_LIBWWW

#include "hslibwww.h"

#include <stdio.h>
#include <string.h>

static const char darcs_version[] = PACKAGE_VERSION;

#include <WWWLib.h>
#include <WWWInit.h>

enum RESULT_CODES
  {
    RESULT_OK = 0,
    RESULT_REQUEST_NEW_FAIL,
    RESULT_NET_ADD_AFTER_FAIL,
    RESULT_REQUEST_ADD_CACHE_CONTROL_FAIL,
    RESULT_LOAD_TO_FILE_FAIL,
    RESULT_MALLOC_FAIL,
    RESULT_LIST_NEW_FAIL,
    RESULT_LIST_APPEND_OBJECT_FAIL,
    RESULT_EVENTLIST_NEW_LOOP
  };

static const char *error_strings[] =
  {
    "",
    "HTRequest_new() failed",
    "HTNet_addAfter() failed",
    "HTRequest_addCacheControl() failed",
    "HTLoadToFile() failed",
    "malloc() failed",
    "HTList_new() failed",
    "HTList_appendObject() failed",
    "HTEventList_newLoop() failed"
  };

struct Completed
{
  int error;
  char *url;
};

static BOOL debug = NO;
static BOOL init_done = NO;
static int error;
static HTList *completed_list = NULL;
static char *last_url = NULL;
static char libwww_error[80];
static const char libwww_error_fmt[] = "libwww error code: %i";

int terminate_handler(HTRequest *request,
                      HTResponse *response,
                      void *param,
                      int status)
{
  struct Completed *completed = malloc(sizeof(struct Completed));
  if (completed == NULL)
    error = RESULT_MALLOC_FAIL;
  else
    {
      if (HTList_appendObject(completed_list, completed) == NO)
        {
          error = RESULT_LIST_APPEND_OBJECT_FAIL;
          free(completed);
        }
      else
        {
          error = RESULT_OK;
          completed->error = status;
          completed->url = HTRequest_context(request);
        }
    }

  HTRequest_delete(request);
  HTEventList_stopLoop();

  // Return not HT_OK to stop executing after filters.
  return HT_ERROR;
}

const char *libwww_request_url(const char *url,
                               const char *filename,
                               int cache_time)
{
  BOOL result;

  if (init_done == NO)
    {
      HTProfile_newNoCacheClient("darcs", darcs_version);
      HTProxy_getEnvVar();
      HTAlert_setInteractive(NO);
      HTFormat_addCoding("*", NULL, HTIdentityCoding, 1.0);
      if (debug == YES)
        HTSetTraceMessageMask("sop");
      init_done = YES;
    }

  if (completed_list == NULL)
    {
      completed_list = HTList_new();
      if (completed_list == NULL)
        return error_strings[RESULT_LIST_NEW_FAIL];
    }

  HTRequest *const request = HTRequest_new();
  if (request == NULL)
    return error_strings[RESULT_REQUEST_NEW_FAIL];

  HTRequest_setContext(request, strdup(url));

  result = HTNet_addAfter(terminate_handler, NULL, NULL, HT_ALL, HT_FILTER_LAST);
  if (result == NO)
    return error_strings[RESULT_NET_ADD_AFTER_FAIL];

  if (cache_time == 0)
    {
      HTRequest_addGnHd(request, HT_G_PRAGMA_NO_CACHE);
      result = HTRequest_addCacheControl(request, "no-cache", "");
    }
  else if (cache_time > 0)
    {
      char buf[8];
      snprintf(buf, sizeof(buf), "%d", cache_time);
      buf[sizeof(buf) - 1] = '\0';
      result = HTRequest_addCacheControl(request, "max-age", buf);
    }
  if (result == NO)
    return error_strings[RESULT_REQUEST_ADD_CACHE_CONTROL_FAIL];

  result = HTLoadToFile(url, request, filename);
  if (result == NO)
    return error_strings[RESULT_LOAD_TO_FILE_FAIL];

  return error_strings[RESULT_OK];
}

const char *libwww_wait_next_url()
{
  if (last_url != NULL)
    {
      free(last_url);
      last_url = NULL;
    }

  error = RESULT_OK;
  if (HTList_isEmpty(completed_list) == YES &&
      HTNet_isEmpty() == NO &&
      HTEventList_newLoop() != HT_OK)
    return error_strings[RESULT_EVENTLIST_NEW_LOOP];

  if (HTList_isEmpty(completed_list) == NO)
    {
      struct Completed *completed = HTList_firstObject(completed_list);
      if (completed->error == HT_LOADED)
        libwww_error[0] = '\0';
      else
        {
          snprintf(libwww_error, sizeof(libwww_error),
                   libwww_error_fmt, completed->error);
          libwww_error[sizeof(libwww_error) - 1] = '\0';
        }
      last_url = completed->url;
      HTList_removeFirstObject(completed_list);
      free(completed);

      return libwww_error;
    }

  return error_strings[error];
}

const char *libwww_last_url()
{
  return last_url != NULL ? last_url : "";
}

void libwww_enable_debug()
{
  debug = YES;
}

#endif
