#include <stdio.h>
#include <sys/time.h>
#include <time.h>	/* to keep glibc 2.2 quiet */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

#include "os.h"
#include "vt.h"
#include "fdset.h"
#include "vbi.h"
#include "lang.h"
#include "misc.h"

int debug = 0;

char *fmt = "%a %b %d %H:%M:%S %Z %Y";
int max_diff = 2*60*60;	// default: 2 hours
int set_time = 0;

static void
chk_time(int t)
{
    struct tm *tm;
    time_t sys_t;
    int dt;
    char buf[256];

    if (t < 0 || t > 235959 || t%100 > 59 || t/100%100 > 59)
	return;

    sys_t = time(0);
    tm = localtime(&sys_t);

    // Now convert to UTC seconds
    t = t/100/100 * 60*60 + t/100%100 * 60 + t%100;
#ifdef BSD
    t -= tm->tm_gmtoff; // dst already included...
#else
    t += timezone;
    if (tm->tm_isdst)
	t -= 60*60;
#endif

    dt = t - sys_t % (24*60*60);
    if (dt <= -12*60*60)
	dt += 24*60*60;

    if (dt <= -max_diff || dt >= max_diff)
	fatal("time diff too big (%2d:%02d:%02d)", dt/60/60, abs(dt)/60%60, abs(dt)%60);

    sys_t += dt;

    if (set_time)
    {
	struct timeval tv[1];

	tv->tv_sec = sys_t;
	tv->tv_usec = 500000;
	if (settimeofday(tv, 0) == -1)
	    ioerror("settimeofday");
    }
    if (*fmt)
    {
	tm = localtime(&sys_t);
	if (strftime(buf, sizeof(buf), fmt, tm))
	    puts(buf);
    }
    exit(0);
}

static void
event(void *_, struct vt_event *ev)
{
    switch (ev->type)
    {
	/* vbi may generate EV_PAGE, EV_HEADER, EV_XPACKET */
	/* for event arguments see vt.h */

	case EV_HEADER: // a new title line (for running headers)
	{
	    static int last_t = -1;
	    u8 *s = ev->p1;
	    int i, t = 1;

	    if (ev->i2 & PG_OUTOFSEQ)
		break;

	    for (i = 32; i < 40; ++i)
		if (s[i] >= '0' && s[i] <= '9')
		    t = t * 10+ s[i] - '0';
	    if (t >= 1000000 && t <= 1235959)
		if (t == last_t || t - last_t == 1)
		    chk_time(t - 1000000);
	    /*
	    if (last_t != t)
		printf("%06d\n", t-1000000);
	    */
	    last_t = t;
	    break;
	}
    }
}


static void
usage(FILE *fp, int exit_val)
{
    fprintf(fp, "usage: %s [options]\n", prgname);
    fprintf(fp,
	    "\n"
	    "  Valid options:\tDefault:\n"
	    "    --help\n"
	    "    --version\n"
	    "    -vbi <vbidev>\n"
	    //"    -oldbttv\n"
	    "    -set\t\toff\n"
	    "    -delta <max_secs>\t7200 (2 hours)\n"
	    "    -format <fmtstr>\t%%c\n"
	    "    -timeout <seconds>\tnone\n"
	    );
    exit(exit_val);
}

static int
option(int argc, char **argv, int *ind, char **arg)
{
    static struct { char *nam, *altnam; int arg; } opts[] = {
	{ "-set", "-s", 0 },
	{ "-delta", "-d", 1 },
	{ "-format", "-f", 1 },
	{ "-vbi", "-dev", 1 },
	{ "-timeout", "-t", 1 },
	{ "--version", "-v", 0 },
	{ "--help", "-h", 0 },
	{ "-newbttv", "-new", 0 },
	{ "-oldbttv", "-old", 0 },
    };
    int i;

    if (*ind >= argc)
	return 0;

    *arg = argv[(*ind)++];
    for (i = 0; i < NELEM(opts); ++i)
	if (streq(*arg, opts[i].nam) || streq(*arg, opts[i].altnam))
	{
	    if (opts[i].arg)
		if (*ind < argc)
		    *arg = argv[(*ind)++];
		else
		    fatal("option %s requires an argument", *arg);
	    return i+1;
	}

    if (**arg == '-')
    {
	fatal("%s: invalid option", *arg);
	usage(stderr, 1);
    }

    return -1;
}

int
main(int argc, char **argv)
{
    char *vbi_name = "/dev/vbi";
    int timeout = 0;
    int big_buf = -1;
    struct vbi *vbi;
    int opt, ind;
    char *arg;

    setprgname(argv[0]);

    ind = 1;
    while (opt = option(argc, argv, &ind, &arg))
	switch (opt)
	{
	    case 1:	// -set
		set_time = 1;
		break;

	    case 2:	// -delta
		max_diff = atoi(arg);
		if (max_diff < 1)
		    fatal("-delta: illegal value '%s'", arg);
		if (max_diff > 12*60*60)
		{
		    max_diff = 12*60*60;
		    error("-delta: %d too big.  assuming %d", arg, max_diff);
		}
		break;
	    case 3:	// -fmt
		fmt = arg;
		break;
	    case 4:	// -vbi
		vbi_name = arg;
		break;
	    case 5:	// -timeout
		timeout = atoi(arg);
		if (timeout < 1 || timeout > 60*60)
		    fatal("-timeout: illegal value '%s'", arg);
		break;
	    case 6:	// -version
		printf("AleVT-Date Version "VERSION"\n");
		exit(0);
	    case 7:	// help
		usage(stdout, 0);
		break;
	    case 8:	// newbttv
		big_buf = 1;
		break;
	    case 9:	// oldbttv
		big_buf = 0;
		break;
	    case -1:
		usage(stderr, 1);
		break;
	}

    fdset_init(fds);

    if (timeout)
    {
	signal(SIGALRM, SIG_DFL);	// kill me
	alarm(timeout);
    }

    vbi = vbi_open(vbi_name, 0, 1, big_buf);	// open device
    if (not vbi)
	fatal_ioerror(vbi_name);

    vbi_add_handler(vbi, event, 0);	// register event handler

    for (;;)
	fdset_select(fds, -1);		// call scheduler

    /* never reached */
    vbi_del_handler(vbi, event, 0);
    vbi_close(vbi);
    exit(0);
}
