#include "../continuity/include/csys.h"
#include "../continuity/mecha/dyn.h"
#include "../continuity/mecha/net.h"
#include "../continuity/include/continuity.h"
#include "../continuity/mecha/lst.h"
#include <http.h>
#include <cmd.h>
#include "site.h"

static const char *logroot = NULL;      /* Path to logging root */

#define LOG_AUX 1
#define LOG_ACCESS 0
    
struct logSfile {
    int day;
    int month;  
    int year;
    int hour;   
    int class;
    FILE *fh;
    unsigned int last_used;
    
                
    struct logSfile *next;
        
};
typedef struct logSfile logTfile;
        
static char hostname[64];
static size_t hostlen;

static logTfile *logGfile = NULL;
    
static pthread_mutex_t logCfile = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t logCfopen = PTHREAD_MUTEX_INITIALIZER;

int siteFlog_init(void);
static logTfile *logFopen_file(int m, int d, int y, int h, int class);
static logTfile *logFcur_file(int class);
static void logFfile_add(logTfile * f);
static void logFfile_free(logTfile * p);
static int logFreap(int delta);
void logFfile_remove(logTfile * f);
static FILE *logFget_cur_fh(int class);
static int logFwrite(int class, char *format, va_list ap);
int siteFaccess_log(httpTtrans * t, MECHA_UNUSED_ARGUMENT lstTset * opts);

void siteFset_logroot(const char *lr) {
      logroot=lr;
}

int siteFlog_init(void)
{
    gethostname(hostname, sizeof(hostname));
    hostlen = strlen(hostname);

    return STATUS_OK;
}

static logTfile *logFopen_file(int m, int d, int y, int h, int class)
{
    char buf[1024];
    logTfile *lf;
    int trycount = 5;   
        
    logFreap(3600);
         
    if (logroot == NULL)
        return NULL;
        
    /* access-04.24.2002.10 */
    
    if (class == 1) {
        snprintf(buf, sizeof(buf), "%s/aux-%02d.%02d.%d.%02d", logroot, m,
                 d, y, h);
    } else {
        snprintf(buf, sizeof(buf), "%s/access-%02d.%02d.%d.%02d", logroot,
                 m, d, y, h);
    }

    lf = (logTfile *) malloc(sizeof(logTfile));
    bzero(lf, sizeof(logTfile));
    
    lf->month = m;
    lf->day = d;
    lf->year = y;
    lf->hour = h;
    lf->class = class;
    lf->last_used = utlFtime();

    while (trycount != 0) {
        lf->fh = fopen(buf, "a");

        if (lf->fh == NULL) {
            logFmsg(2,
                    "logFopen_file: fopen failure (fn:%s) (er: %d) (tc: %d)",
                    buf, errno, trycount);
            logFreap(0);
        } else {
            break;
        }
                
        trycount--;
    }   
  
    if (lf->fh == NULL) {
        logFmsg(2, "Unable to open log after 5 tries, giving up (fn:%s)",
                buf);
        free(lf);
        return NULL;
    }
    return lf;
}

/*  
 * The world was young, the mountains green, No stain yet on the Moon was
 * seen, No words were laid on stream or stone When Durin woke and walked
 * alone. He named the nameless hills and dells; He drank from yet untasted
 * wells; He stooped and looked in Mirrormere And saw a crown of stars
 * appear; As gems upon a silver thread; Above the shadow of his head.
 *
 * The world is grey, the mountains old, The forge's fire is ashen-cold; No harp
 * is wrung, no hammer falls: The darkness dwells in Durin's halls; The
 * shadow lies upon his tomb In Moria, in Khazad-d?m. But still the sunken
 * stars appear In dark and windless Mirrormere; There lies his crown in
 * water deep, Till Durin wakes again from his sleep.
 */
    
static logTfile *logFcur_file(int class)
{
    logTfile *p;
    const struct tm *tme;
        
    pthread_mutex_lock(&logCfile);
    
    if (logGfile == NULL) {
        pthread_mutex_unlock(&logCfile);
        return NULL;
  }
    p = logGfile;
    tme = (const struct tm *) utlFlocaltime();
                 
    while (1) {
        if ((p->month == (tme->tm_mon + 1)) &&
            (p->day == tme->tm_mday) &&
            (p->year == (tme->tm_year + 1900)) &&
            (p->hour == tme->tm_hour) && (p->class == class)) {
            pthread_mutex_unlock(&logCfile);
            return p;
        }
        if (p->next == NULL) {
            pthread_mutex_unlock(&logCfile);
            return NULL;
        }
        p = p->next;
    }
}
        
static void logFfile_add(logTfile * f)
{
    pthread_mutex_lock(&logCfile);
    f->next = logGfile; 
    logGfile = f;
    pthread_mutex_unlock(&logCfile);
}
                
static void logFfile_free(logTfile * p)
{
    if (p != NULL)
        free(p);
}
                
/*
 * logFreap - Close logfiles that haven't been used for some period of time.
 */  
static int logFreap(int delta)
{
    logTfile *p, *next;
    struct timeval tv;
    int i;
 
    pthread_mutex_lock(&logCfile);
 
    if (logGfile == NULL) {
        pthread_mutex_unlock(&logCfile);
        return 0;
    }
    p = logGfile;
 
    while (p != NULL) {
        gettimeofday(&tv, NULL);
    
        next = p->next;
 
        if ((tv.tv_sec - p->last_used) >= (unsigned int) delta) {
            logGfile = p->next;
        
            fclose(p->fh);
    
            logFfile_free(p);
            i++;
        }
        p = next;
    }
    
    pthread_mutex_unlock(&logCfile);
    
    return i;
}
            
#if 0
/*
 * logFfile_remove - Remove a logfile reference from the global list
 *
 * This function requires that the global list contain at least one entry and
 * does not return an error if the argument entry is not found in the list.
 */
         
void logFfile_remove(logTfile * f)
{
    logTfile *p;
        
    pthread_mutex_lock(&logCfile);
 
    p = logGfile;
    
    while (1) {  
        if (p == f) {
            logGfile = p->next;
            break;
        }
        p = p->next;
        if (p == NULL)
            break;
    }
    pthread_mutex_unlock(&logCfile);
} 
#endif
     
static FILE *logFget_cur_fh(int class)
{
    logTfile *file;
    
    pthread_mutex_lock(&logCfopen);
 
    file = logFcur_file(class);   
 
    if (file == NULL) {
        const struct tm *tme;
        tme = (const struct tm *) utlFlocaltime();
        file =
            (logTfile *) logFopen_file(tme->tm_mon + 1, tme->tm_mday,
                                       tme->tm_year + 1900, tme->tm_hour,
                                       class);
        if (file == NULL) {
            pthread_mutex_unlock(&logCfopen);
            return NULL;
        }
        logFfile_add(file);
    }
    file->last_used = utlFtime();
            
    pthread_mutex_unlock(&logCfopen);
    return file->fh;
}
         
int siteFaccess_log(httpTtrans * t, MECHA_UNUSED_ARGUMENT lstTset * opts)
{
    FILE *fh;
    char buf[1024];
    dynTstring *logline;
    int ret;
            
#ifdef MODSITE_DEBUG
    logFmsg(3,"siteFaccess_log: called");
#endif

    fh = logFget_cur_fh(LOG_ACCESS);

    if (fh == NULL) {
        logFmsg(2, "logFget_cur_fh failed (er: %d)", errno);
        return STATUS_ERROR;
    }
    logline = dynFinit();
    
    /* CLIENT IP */
    utlFip_to_str(t->cli_ipv4_addr, buf, sizeof(buf));
    dynFsappend(logline, buf);
    dynFappend(logline, " ", 1);
        
    /* EPOCH */
    {
        const char *bp = (const char *) utlFcommonlog_time();
        dynFappend(logline, bp, 29);
    }
            
    /* REQUEST */
    dynFappend(logline, "\"", 1);
     
    {
            
        const lstTfield *clf =
            lstFset_get_field(t->vars, "clf-request", 11);
        if (clf != NULL) {
            dynFappend(logline, lstFfield_value(clf),
                       lstFfield_value_len(clf));
        } else {
            dynFappend(logline, "REQ_ERR", 7);
        }
    
        dynFappend(logline, "\" ", 2);
    }
    
    /* STATUS */
    /*
     * snprintf(buf, sizeof(buf), "%d ", t->res_code); dynFappend(logline,
     * buf, strlen(buf));
     */
    dynFappend_print(logline, "%d ", t->res_code);
    
    {
        const lstTfield *length =
            lstFset_get_field(t->res_hdrs, "Content-Length", 14);
    
        if (length != NULL) {
            dynFappend(logline, lstFfield_value(length),
                       lstFfield_value_len(length));
            dynFappend(logline, " ", 1);
        } else {
            dynFappend(logline, "- ", 2);
        }
    }
    
    {
        const lstTfield *referer =
            lstFset_get_field(t->req_hdrs, "referer", 7);

        if (referer != NULL) {
            dynFappend(logline, "\"", 1);
            dynFappend(logline, lstFfield_value(referer),
                       lstFfield_value_len(referer));
            dynFappend(logline, "\" ", 2);
        } else {
            dynFappend(logline, "\"\" ", 3);
        }
    }
    
    {
        const lstTfield *agent =
            lstFset_get_field(t->req_hdrs, "user-agent", 10);
    
        if (agent != NULL) {
            dynFappend(logline, "\"", 1);
            dynFappend(logline, lstFfield_value(agent),
                       lstFfield_value_len(agent));
            dynFappend(logline, "\" ", 2);
        } else {
            dynFappend(logline, "\"\" ", 3);
        }   
    }
    
    /* Country Code Placeholder */
    {
        const lstTfield *ccode = lstFset_get_field(t->vars, "ccode", 5);
        
        if (ccode != NULL) {
            dynFappend(logline, lstFfield_value(ccode),
                       lstFfield_value_len(ccode));

          dynFappend(logline, " ", 1);
        } else {
            dynFappend(logline, "-1 ", 3);
        }
    }
    
    /* Referer target URL: http://host/URI */
    {
        const lstTfield *uri =
            lstFset_get_field(t->res_hdrs, "Location", 8);
      
        if (uri != NULL) {
            dynFappend(logline, "\"", 1);
            dynFappend(logline, lstFfield_value(uri),
                       lstFfield_value_len(uri)); 
            dynFappend(logline, "\" ", 2);
        } else {
            dynFappend(logline, "- ", 2);
        }
    }
        
    /* Response Time Placeholder */
    dynFappend(logline, "-1 ", 3);
            
    {
        const lstTfield *user = lstFset_get_field(t->vars, "auth-user", 9);
        if (user != NULL) {
            dynFappend(logline, lstFfield_value(user),
                       lstFfield_value_len(user));
            dynFappend(logline, " ", 1);
        } else {
            dynFappend(logline, "- ", 2);
        }
    }
            
    {
        const lstTfield *siteid = lstFset_get_field(t->vars, "siteid", 6);
            
        if (siteid != NULL) {
            dynFappend(logline, lstFfield_value(siteid),
                       lstFfield_value_len(siteid));
        } else {
            dynFappend(logline, "_SID_ERROR", 10);
        }
    }
            
    dynFappend(logline, " ", 1);
    dynFappend(logline, hostname, hostlen);
            
    dynFappend(logline, "\n", 1);
                       
    ret = fwrite(dynFgetstr(logline),dynFgetlen(logline),1,fh);
        
    if (ret < 0 || ret == EOF) {
        logFmsg(1, "Log write unexpectadly returned %d/%d", ret, errno);
    }
    fflush(fh);
    
    dynFfree(logline);
        
    return ret;
}

