/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */

/*  Monkey HTTP Server
 *  ==================
 *  Copyright 2001-2015 Monkey Software LLC <eduardo@monkey.io>
 *
 *  Licensed 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 <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>

#include <monkey/mk_core.h>
#include <monkey/mk_config.h>
#include <monkey/mk_clock.h>
#include <monkey/mk_utils.h>

time_t log_current_utime;
time_t monkey_init_time;

mk_ptr_t log_current_time = { NULL, LOG_TIME_BUFFER_SIZE - 2 };
mk_ptr_t headers_preset = { NULL, HEADER_PRESET_SIZE - 1 };

static char *log_time_buffers[2];
static char *header_time_buffers[2];

/* The mk_ptr_ts have two buffers for avoid in half-way access from
 * another thread while a buffer is being modified. The function below returns
 * one of two buffers to work with.
 */
static inline char *_next_buffer(mk_ptr_t *pointer, char **buffers)
{
    if(pointer->data == buffers[0]) {
        return buffers[1];
    } else {
        return buffers[0];
    }
}

static void mk_clock_log_set_time(time_t utime)
{
    char *time_string;
    struct tm result;

    time_string = _next_buffer(&log_current_time, log_time_buffers);
    log_current_utime = utime;

    strftime(time_string, LOG_TIME_BUFFER_SIZE, "[%d/%b/%G %T %z]",
             localtime_r(&utime, &result));

    log_current_time.data = time_string;
}

static void mk_clock_headers_preset(time_t utime)
{
    int len1;
    int len2;
    struct tm *gmt_tm;
    struct tm result;
    char *buffer;

    buffer = _next_buffer(&headers_preset, header_time_buffers);

    gmt_tm = gmtime_r(&utime, &result);

    len1 = snprintf(buffer,
                    HEADER_TIME_BUFFER_SIZE,
                    "%s",
                    mk_config->server_signature_header);

    len2 = strftime(buffer + len1,
                    HEADER_PRESET_SIZE - len1,
                    MK_CLOCK_GMT_DATEFORMAT,
                    gmt_tm);

    headers_preset.data = buffer;
    headers_preset.len  = len1 + len2;
}

void *mk_clock_worker_init(void *args UNUSED_PARAM)
{
    time_t cur_time;

    mk_utils_worker_rename("monkey: clock");
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

    mk_clock_tid = pthread_self();

    while (1) {
        cur_time = time(NULL);

        if(cur_time != ((time_t)-1)) {
            mk_clock_log_set_time(cur_time);
            mk_clock_headers_preset(cur_time);
        }
        sleep(1);
    }

    return NULL;
}

void mk_clock_exit()
{
    pthread_cancel(mk_clock_tid);
    pthread_join(mk_clock_tid, NULL);

    mk_mem_free(header_time_buffers[0]);
    mk_mem_free(header_time_buffers[1]);
    mk_mem_free(log_time_buffers[0]);
    mk_mem_free(log_time_buffers[1]);
}

/* This function must be called before any threads are created */
void mk_clock_sequential_init()
{
    /* Time when monkey was started */
    monkey_init_time = time(NULL);

    header_time_buffers[0] = mk_mem_malloc_z(HEADER_PRESET_SIZE);
    header_time_buffers[1] = mk_mem_malloc_z(HEADER_PRESET_SIZE);

    log_time_buffers[0] = mk_mem_malloc_z(LOG_TIME_BUFFER_SIZE);
    log_time_buffers[1] = mk_mem_malloc_z(LOG_TIME_BUFFER_SIZE);


    /* Set the time once */
    time_t cur_time = time(NULL);

    if (cur_time != ((time_t)-1)) {
        mk_clock_log_set_time(cur_time);
        mk_clock_headers_preset(cur_time);
    }
}
