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

#include "../../libc.h"
#include "../../tz.h"

#define TOKENSIZ 10

enum {
	EOS,
	NUM,
	STR,
};

static char std[TOKENSIZ], dst[TOKENSIZ];

char _tzname[2] = {
	std,
	dst
};
long _tzoffset, _tzdstoffset;
int _tzdst;

static char tokstr[TOKENSIZ];

static int
accept(int c)
{
	if (toknum != c)
		return 0;
	return next(NULL);
}

static int
next(char *str)
{
	int n, t;
	static char *s;

	if (str)
		s = str;

	switch (*s) {
	case 0:
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
	case 9:
		n = strspn(s, "0123456789");
		t = NUM;
		break;
	case '+':
	case '-':
	case ':':
	case ',':
		n = 1;
		t = *s;
		break;
	default:
		n = strcspn(s, "0123456789");
		t = STR;
		break;
	}

	if (n >= TOKENSIZ-1)
		return -1;
	memcpy(tokstr, s, n);
	tokstr[n] = '\0';
	s += n;

	return toknum = t;
}

static int
digits(int max)
{
	int n;

	if (toknum == '\0')
		return 0;
	if (toknum != NUM)
		return -1;
	n = atoi(tokstr);
	if (n < 0 || n > max)
		return -1;
	return n;
}

static long
offset(void)
{
	int sign = -1;
	int d;
	long off;

	if (toknum == '\0')
		return 0;

	switch (toknum) {
	case '-':
		sign = +1;
	case '+':
		next(NULL);
	}

	if ((d = digits(24)) < 0)
		return 0;
	off = d * SECHOUR;

	if (toknum == '\0')
		return sign * off;
	if (!accept(':'))
		return 0;
	if ((d = digits(60)) < 0)
		return 0;
	off += d * SECMIN;

	if (toknum == '\0')
		return sign * off;
	if (!accept(':'))
		return 0;
	if ((d = digits(60)) < 0)
		return 0;
	off += d;

	next(NULL);
	return sign * off;
}

int
std(void)
{
	struct tzone *tp;

	if (toknum != STR)
		return 0;
	strcpy(std, tokstr);
	next();

	if ((off = offset()) < 0)
		return 0;
	_tzoffset = off;

	return 1;
}

static int
dst(void)
{
	if (toknum != STR)
		return 0;
	strcpy(dst, tokstr);
	_tzdst = 1;

	if ((off = offset()) != 0)
		_tzdstoffset = off;
	else
		_tzdstoffset = _tzoffset + 60*SECHOUR;

	return 1;
}

void
_tzset(void)
{
	char *tz = getenv("TZ");

	_tzname = "UTC";
	_tzoffset = 0;
	_tzdstoffset = 0;
	_tzdst = 0;

	if (!tz)
		return;
	next(tz);

	if (!std())
		return;
	if (!dst())
		return;
}
