/** @file

    Main file for the tstop application.

    @section license License

    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "base.h"
#include "log.h"
#include "buffer.h"

#include "plugin.h"

#include "response.h"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>

/**
 * this is a generator for a lighttpd plugin
 *
 * just replaces every occurance of 'generator' by your plugin name
 *
 * e.g. in vim:
 *
 *   :%s/generator/myhandler/
 *
 */

static char static_data[8192];

/* plugin config for all request/connections */

typedef struct {
  array *match;
} plugin_config;

typedef struct {
  PLUGIN_DATA;

  buffer *match_buf;

  plugin_config **config_storage;

  plugin_config conf;

} plugin_data;

typedef struct {
  size_t foo;
} handler_ctx;

// static handler_ctx * handler_ctx_init() {
// 	handler_ctx * hctx;

// 	hctx = calloc(1, sizeof(*hctx));

// 	return hctx;
// }

// static void handler_ctx_free(handler_ctx *hctx) {

// 	free(hctx);
// }

/* init the plugin data */
INIT_FUNC(mod_generator_init) {
  plugin_data *p;

  p = calloc(1, sizeof(*p));

  p->match_buf = buffer_init();

  return p;
}

/* detroy the plugin data */
FREE_FUNC(mod_generator_free) {
  plugin_data *p = p_d;

  UNUSED(srv);

  if (!p) return HANDLER_GO_ON;

  if (p->config_storage) {
    size_t i;

    for (i = 0; i < srv->config_context->used; i++) {
      plugin_config *s = p->config_storage[i];

      if (!s) continue;

      array_free(s->match);

      free(s);
    }
    free(p->config_storage);
  }

  buffer_free(p->match_buf);

  free(p);

  return HANDLER_GO_ON;
}

/* handle plugin config and check values */

SETDEFAULTS_FUNC(mod_generator_set_defaults) {
  plugin_data *p = p_d;
  size_t i = 0;

  config_values_t cv[] = {
    { "generator.array",             NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
    { NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  };

  if (!p) return HANDLER_ERROR;

  p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));

  for (i = 0; i < srv->config_context->used; i++) {
    plugin_config *s;

    s = calloc(1, sizeof(plugin_config));
    s->match    = array_init();

    cv[0].destination = s->match;

    p->config_storage[i] = s;

    if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
      return HANDLER_ERROR;
    }
  }

  return HANDLER_GO_ON;
}

#define PATCH(x)                                \
  p->conf.x = s->x;
static int mod_generator_patch_connection(server *srv, connection *con, plugin_data *p) {
  size_t i, j;
  plugin_config *s = p->config_storage[0];

  PATCH(match);

  /* skip the first, the global context */
  for (i = 1; i < srv->config_context->used; i++) {
    data_config *dc = (data_config *)srv->config_context->data[i];
    s = p->config_storage[i];

    /* condition didn't match */
    if (!config_check_cond(srv, con, dc)) continue;

    /* merge config */
    for (j = 0; j < dc->value->used; j++) {
      data_unset *du = dc->value->data[j];

      if (buffer_is_equal_string(du->key, CONST_STR_LEN("generator.array"))) {
        PATCH(match);
      }
    }
  }

  return 0;
}
#undef PATCH

URIHANDLER_FUNC(mod_generator_uri_handler) {
  plugin_data *p = p_d;
  int s_len;
  size_t k;

  UNUSED(srv);

  if (con->mode != DIRECT) return HANDLER_GO_ON;

  if (con->uri.path->used == 0) return HANDLER_GO_ON;

  mod_generator_patch_connection(srv, con, p);

  s_len = con->uri.path->used - 1;

  for (k = 0; k < p->conf.match->used; k++) {
    data_string *ds = (data_string *)p->conf.match->data[k];
    int ct_len = ds->value->used - 1;

    if (ct_len > s_len) continue;
    if (ds->value->used == 0) continue;

    if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
      con->http_status = 403;

      return HANDLER_FINISHED;
    }
  }

  /* not found */
  return HANDLER_GO_ON;
}


URIHANDLER_FUNC(mod_generator_subrequest_handler) {
  (void) p_d;
  //plugin_data *p = p_d;
  buffer *b;
  b = chunkqueue_get_append_buffer(con->write_queue);


  // get the url information
  //int length = strlen(con->uri.path->ptr);
  char *start = con->uri.path->ptr;
  char *end;
  //  char *end = start + length;
  if (*start != '/') {
    log_error_write(srv, __FILE__, __LINE__,  "s", "url doesn't start with a slash");
    return HANDLER_GO_ON;
  }
  ++start;

  // get the size in bytes form the url
  int64_t bytes = strtoll(start, &end, 10);

  switch (*end) {
  case 'k':
  case 'K':
    bytes *= 1024;
    ++end;
    break;
  case 'm':
  case 'M':
    bytes *= 1024*1024;
    ++end;
    break;
  case 'g':
  case 'G':
    bytes *= 1024*1024*1024;
    ++end;
    break;
  default:
    break;
  }

  if (start == end && bytes <= 0 && *start != '-') {
    log_error_write(srv, __FILE__, __LINE__,  "s", "can't find size in bytes");
    return HANDLER_GO_ON;
  }
  start = end + 1;

  // get the id from the url
  int64_t id = strtoll(start, &end, 10);

  if (start == end && id < 0 && *start != '-') {
    log_error_write(srv, __FILE__, __LINE__,  "s", "problems finding the id");
    return HANDLER_GO_ON;
  }
  start = end + 1;

  // get the time to sleep from the url
  int64_t sleepval = strtoll(start, &end, 10);

  if (start == end && id < 0 && *start != '-') {
    log_error_write(srv, __FILE__, __LINE__,  "s", "problems finding the id");
    return HANDLER_GO_ON;
  }
  start = end + 1;
  if (sleepval > 0) {
    usleep(1000 * sleepval);
  }

  // check to see if we are going to set cacheable headers
  int cache = -1;
  if (strcmp(start, "cache") == 0) {
    cache = 1;
  } else if (strcmp(start, "no_cache") == 0) {
    cache = 0;
  } else {
    log_error_write(srv, __FILE__, __LINE__,  "s", "didn't see cache or no_cache in the url");
    return HANDLER_GO_ON;
  }

  // print the body of the message
  uint64_t to_write = 0;
  --bytes;              // leave a char left over for \n
  while (bytes > 0) {
    if ((uint64_t)bytes > sizeof(static_data)) {
      // biger then the static buffer, so write the entire buffer
      to_write = sizeof(static_data);
    } else {
      to_write = bytes;
    }

    buffer_append_string_len(b, static_data, to_write);
    bytes -= to_write;
  }

  if (bytes == 0) {
    buffer_append_string_len(b, "\n", 1); // add a \n to the end of the body
  }

  // write the headers if it is cacheable or not
  if (cache == 0) {
    response_header_insert(srv, con, CONST_STR_LEN("Cache-Control"), CONST_STR_LEN("private"));
  } else {
    response_header_insert(srv, con, CONST_STR_LEN("Last-Modified"), CONST_STR_LEN("Thu, 12 Feb 2009 23:00:00 GMT"));
    response_header_insert(srv, con, CONST_STR_LEN("Cache-Control"), CONST_STR_LEN("max-age=86400, public"));
  }

  con->http_status = 200;
  con->file_finished = 1;
  return HANDLER_FINISHED;
}

/* this function is called at dlopen() time and inits the callbacks */

int mod_generator_plugin_init(plugin *p) {

  p->version     = LIGHTTPD_VERSION_ID;
  p->name        = buffer_init_string("generator");

  p->init        = mod_generator_init;
  p->handle_uri_clean  = mod_generator_uri_handler;
  p->handle_physical  = mod_generator_subrequest_handler;
  p->set_defaults  = mod_generator_set_defaults;
  p->cleanup     = mod_generator_free;

  p->data        = NULL;

  // set the static data used in the response;
  memset(static_data, 'x', sizeof(static_data));

  return 0;
}
