/* Copyright (C) 2010,2013 G.P. Halkes
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 3, as
   published by the Free Software Foundation.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <float.h>
#include <time.h>
#include <errno.h>

#include "common.h"
#include "optionMacros.h"
#include "replay.h"
#include "input.h"

/* TODO:
- perhaps we should empty the input buffer as well and even set no-echo and
  non-canonical mode
- wait for signal before continuing after set_window_size (if we do the
  actual resize that is). However, we first must check whether we actually get
  a signal!
*/

static int key_delay;
static double key_delay_scale = 1.0;
static const char *input;
static td_bool resize_capable;

static void printUsage(void) {
	printf("Usage: tdview [OPTIONS] <recording>\n");
	printf("  -k<delay>, --key-delay=<delay>      Set key delay\n");
	printf("  -V, --version                       Print version and copyright information\n");
	exit(EXIT_SUCCESS);
}

static PARSE_FUNCTION(parse_options)
	OPTIONS
		OPTION('k', "key-delay", REQUIRED_ARG)
			while (*optArg && isspace(*optArg))
				optArg++;
			if (optArg[0] == '/') {
				optArg++;
				PARSE_DOUBLE(key_delay_scale, DBL_MIN, DBL_MAX);
				key_delay = 0;
			} else if (isdigit(*optArg)) {
				PARSE_INT(key_delay, 1, INT_MAX);
				key_delay_scale = 1.0;
			} else {
				fatal("Invalid argument for key-delay option: %s\n", optArg);
			}
		END_OPTION
		OPTION('h', "help", NO_ARG)
			printUsage();
		END_OPTION
		OPTION('V', "version", NO_ARG)
			fputs("tdview " VERSION_STRING "\nCopyright (C) 2010,2013 G.P. Halkes\nLicensed under the GNU General Public License version 3\n", stdout); /* @copyright */
			exit(0);
		END_OPTION

		DOUBLE_DASH
			NO_MORE_OPTIONS;
		END_OPTION

		fatal("Unknown option " OPTFMT "\n", OPTPRARG);
	NO_OPTION
		if (input != NULL)
			fatal("More than one recording specified\n");
		input = optcurrent;
	END_OPTIONS
	if (input == NULL)
		fatal("No recording specified\n");
END_FUNCTION


static void print_string_list(StringListNode *string) {
	for (; string != NULL; string = string->next)
		safe_write(STDOUT_FILENO, string->string, string->length);
}

static void do_delay(int delay) {
	struct timespec delay_spec;

	if (delay == 0)
		return;
	else if (key_delay)
		delay = key_delay;
	else
		delay = delay / key_delay_scale;

	delay_spec.tv_nsec = (long) (delay % 1000) * 1000000;
	delay_spec.tv_sec = delay / 1000;
	while (nanosleep(&delay_spec, &delay_spec) < 0) {
		if (errno == EINTR)
			continue;
		fatal("Error sleeping: %s\n", strerror(errno));
	}
}


static void set_window_size(ExpNode *size) {
	if (resize_capable) {
		char buffer[64];
		size_t result;
		/* FIXME: this should use tparm or similar to ensure proper padding etc. */
		result = sprintf(buffer, "\x1b[8;%d;%dt", size->un.size.rows, size->un.size.columns);
		safe_write(STDOUT_FILENO, buffer, result);
		/* FIXME: wait for terminal to respond because otherwise the next updates may fail. */
	}
}

int tdview_main(int argc, char *argv[]) {

	parse_options(argc, argv);

	if (!isatty(STDIN_FILENO))
		fatal("Can only replay to a terminal devices\n");
	resize_capable = detect_resize_capable();

	reset_lexer(input);
	parse();

	save_tty();
	copy_attrs_to_terminal(STDIN_FILENO);

	while (script != NULL) {
		switch (script->type) {
			case EXP_START:
			case EXP_INTERACT:
			case EXP_ENV:
				/* Ignore stuff that isn't relavant to simply viewing the replay. */
				break;
			case EXP_EXPECT:
				print_string_list(script->un.args);
				break;
			case EXP_SEND: {
				StringListNode *key;

				for (key = script->un.args; key != NULL; key = key->next)
					do_delay(key->delay);
				break;
			}
			case EXP_EXPECT_EXIT:
				exit(EXIT_SUCCESS);
				break;
			case EXP_WINDOW_SIZE:
				do_delay(script->un.size.delay);
				set_window_size(script);
				break;
			case EXP_EXPECT_SUSPEND:
				printf("Process has stopped. Will continue in 3 seconds\n");
				sleep(3);
				break;
			case EXP_REQUIRE_VERSION:
				break;
			default:
				fatal("Internal error\n");
		}
		script = script->next;
	}
	return EXIT_SUCCESS;
}
