#include <stdio.h>
#include <limits.h>
#include <err.h>
#include <apr_strings.h>
#include <apr_time.h>
#include <apr_lib.h>

#include "htdavlock.h"

#define HTDAVLOCK_DEPTH_MAX 0x7fffffff

/*
 * From </httpd/mod_dav.h>
 */

typedef enum { 
    DAV_LOCKSCOPE_UNKNOWN,
    DAV_LOCKSCOPE_EXCLUSIVE,
    DAV_LOCKSCOPE_SHARED 
} dav_lock_scope;

typedef enum {
    DAV_LOCKTYPE_UNKNOWN,
    DAV_LOCKTYPE_WRITE
} dav_lock_type;
 
/*
 * From httpd/modules/dav/fs/lock.c
 */
#define DAV_TYPE_FNAME		11

static const char *
htdavlock_scope(dav_lock_scope s, int verbose)
{
	const char *scope = "";

	switch (s) {
    	case DAV_LOCKSCOPE_UNKNOWN:
		scope = verbose ? "UNKNOWN" : "?";
		break;
   	case DAV_LOCKSCOPE_EXCLUSIVE:
		scope = verbose ? "EXCLUSIVE" : "x";
		break;
    	case DAV_LOCKSCOPE_SHARED:
		scope = verbose ? "SHARED" : "s";
		break;
	default:
		errx(1, "unexpected scope %d", s);
		break;
	}

	return scope;
}

static const char *
htdavlock_type(dav_lock_type t, int verbose)
{
	const char *type = "";

	switch (t) {
    	case DAV_LOCKTYPE_UNKNOWN:
		type = verbose ? "UNKNOWN" : "?";
		break;
   	case DAV_LOCKTYPE_WRITE:
		type = verbose ? "WRITE" : "w";
		break;
	default:
		errx(1, "unexpected type %d", t);
		break;
	}

	return type;
}

static const char *
htdavlock_depth(apr_pool_t *p, int d, int verbose)
{
	const char *depth = "";

	if (d == HTDAVLOCK_DEPTH_MAX)
		depth = verbose ? "DEPTH_MAX" : "*";
	else
		depth = apr_psprintf(p, "%d", d);

	return depth;
}

static const char *
htdavlock_timeout(apr_pool_t *p, apr_time_t t, int verbose)
{
	apr_size_t iso_time_len = sizeof("YYYY-mm-dd HH:MM:SS") + 1;
	char *iso_time;
	char *timeout = "";
	apr_time_exp_t lt;
	apr_status_t rv;

	if (t == 0)
		return verbose ? "INFINITE" : "         *        *"; /* pad */

	iso_time = apr_palloc(p, iso_time_len);
	
	if ((rv = apr_time_exp_lt(&lt, apr_time_from_sec(t))) != APR_SUCCESS)
		errx(1, "apr_time_exp_lt failed");

	if ((rv = apr_strftime(iso_time, &iso_time_len, iso_time_len,
			       "%Y-%m-%d %H:%M:%S", &lt)) != APR_SUCCESS)
		errx(1, "apr_strftime failed");

	if (verbose)
		timeout = apr_psprintf(p, "%d (%s)", timeout, iso_time);
	else
		timeout = apr_pstrdup(p, iso_time);

	return timeout;
}

const char *
htdavlock_rectype(char rectype, int verbose)
{
	const char *name = "";

	switch (rectype) {
	case DAV_TYPE_FNAME:
		name = verbose ? "FILENAME" : "-";
		break;
	default:
		errx(1, "unexpected record type %d", rectype);
		break;
	}

	return name;
}
const char *
htdavlock_owner(unsigned char *val)
{
	dav_lock_discovery *d = (dav_lock_discovery *)val;
	return d->pad;
}

const char *
htdavlock_auth_user(unsigned char *val)
{
	dav_lock_discovery *d = (dav_lock_discovery *)val;
	return d->pad + strlen(htdavlock_owner(val)) + 1;
}

const char *
htdavlock_key(dav_lock_indirect *d)
{
	return d->key + 1;
}


static void
htdavlock_dump_discovery(apr_pool_t *p, const char *rec, 
			 unsigned char *val, int verbose)
{
	dav_lock_discovery *d = (dav_lock_discovery *)val;
	char locktoken[APR_UUID_FORMATTED_LENGTH + 1];
	char rectype = rec[0];
	const char *path = rec + 1; 

	apr_uuid_format(locktoken, &d->locktoken);

	if (!verbose) {
		printf("D%s%s %s %10s %s %s\n",
		       htdavlock_scope(d->f.scope, verbose),
		       htdavlock_type(d->f.type, verbose),
		       htdavlock_depth(p, d->f.depth, verbose),
		       htdavlock_auth_user(val),
		       htdavlock_timeout(p, d->f.timeout, verbose),
		       path);
		return;
	}

	printf("%s = \"%s\"\n", htdavlock_rectype(rectype, verbose), path);
	printf("\tlock = DIRECT\n");
	printf("\tscope = %s\n", htdavlock_scope(d->f.scope, verbose));
	printf("\ttype = %s\n", htdavlock_type(d->f.type, verbose));
	printf("\tdepth = %s\n", htdavlock_depth(p, d->f.depth, verbose));
	printf("\ttimeout = %s\n", htdavlock_timeout(p, d->f.timeout, verbose));
	printf("\tlocktoken = \"%s\"\n", locktoken);
	printf("\towner = \"%s\"\n", htdavlock_owner(val));
	printf("\tauth_user = \"%s\"\n", htdavlock_auth_user(val));

	return;
}

static void
htdavlock_dump_indirect(apr_pool_t *p, const char *rec,
			unsigned char *val, int verbose)
{
	dav_lock_indirect *d = (dav_lock_indirect *)val;
	char rectype = rec[0];
	const char *path = rec + 1; 
	char locktoken[APR_UUID_FORMATTED_LENGTH + 1];
	int i;
 
	apr_uuid_format(locktoken, &d->locktoken);

	if (!verbose) {
		printf("I-- -          - %s %s -> %s\n",
		       htdavlock_timeout(p, d->timeout, verbose),
		       path,
		       htdavlock_key(d));
		return;
	}

	printf("%s = \"%s\"\n", htdavlock_rectype(rectype, verbose), path);
	printf("\tlock = INDIRECT\n");
	printf("\tlocktoken = \"%s\"\n", locktoken);
	printf("\ttimeout = %s\n", htdavlock_timeout(p, d->timeout, verbose));
	printf("\tindirect_key %s = \"%s\"\n", 
	       htdavlock_rectype(d->key[0], verbose), htdavlock_key(d));

	return;
}

void
htdavlock_dump(apr_pool_t *p, const char *rec,
	       unsigned char rectype, unsigned char *val, int verbose)
{
	switch (rectype) {
	case DAV_LOCK_DIRECT:
		htdavlock_dump_discovery(p, rec, val, verbose);
		break;
	case DAV_LOCK_INDIRECT:
		htdavlock_dump_indirect(p, rec, val, verbose);
		break;
	default:
		errx(1, "unexpected rectype %d", rectype);
		break;
	}

	return;
}

const char *
htdavlock_locktoken(apr_pool_t *p, unsigned char rectype, unsigned char *val)
{
	apr_uuid_t *uuid = NULL;
	char *locktoken;
	dav_lock_discovery *d;
	dav_lock_indirect *i;

	locktoken = apr_palloc(p, APR_UUID_FORMATTED_LENGTH + 1);

	switch (rectype) {
	case DAV_LOCK_DIRECT:
		d = (dav_lock_discovery *)val;
		apr_uuid_format(locktoken,  &d->locktoken);
		break;
	case DAV_LOCK_INDIRECT:
		i = (dav_lock_indirect *)val;
		apr_uuid_format(locktoken,  &i->locktoken);
		break;
	default:
		errx(1, "unexpected rectype %d", rectype);
		break;
	}
 
	return locktoken;
}
