use libtui;
use libtui::widget;
use libtui::widget::list;
use libtui::layout;
use encoding::utf8;
use io;
use fmt;
use os;
use strings;
use unix::tty;
use unix::signal;
use unix::poll;
use errors;
use getopt;

let searchterm: str = "";
let searchforward: bool = true;

let nulterm: bool = false;
let msg: str = "";

fn resize(list: *list::listwidget) void = {
	let sz = libtui::getwinsize(list.widget.ui)!;
	let rows: (u16 | size) = if (sz.rows - 1 < len(list.items)) {
		yield sz.rows - 1;
	} else {
		yield len(list.items);
	};
	const oldsz = list::ttysize {
		rows = list.sz.rows,
		cols = list.sz.cols,
	};
	let rows: (u16 | size) = if (sz.rows - 1u16 < len(list.items)) {
		yield sz.rows - 1u16;
	} else {
		yield len(list.items);
	};
	list::setsize(list, tty::ttysize {
		rows = rows: u16,
		columns = sz.columns,
	});
	list::resize(list, oldsz);
};

export fn main() void = {
	const cmd = getopt::parse(os::args,
		"interactive list",
		('0', "NUL terminated output"),
		('m', "msg", "string to print when 'h' is pressed (this can be used for custom help messages or building basic extra external features)"),
		('s', "query", "search for a given item when opening the il"),
	);
	defer getopt::finish(&cmd);

	for (let i = 0z; i < len(cmd.opts); i += 1) {
		const opt = cmd.opts[i];
		switch (opt.0) {
		case '0' =>
			nulterm = true;
		case 'm' =>
			msg = opt.1;
		case 's' =>
			searchterm = strings::dup(opt.1);
		case =>
			yield;
		};
	};

	let in = match (io::drain(os::stdin)) {
	case let in: []u8 =>
		yield in;
	case let e: io::error =>
		fmt::fatal(io::strerror(e));
	};
	defer free(in);
	let sin = strings::fromutf8(in)!;
	sin = strings::trim(sin, '\n');
	let items = strings::split(sin, "\n"); // freed by list::finish
	itemscopy = strings::dupall(items);
	defer strings::freeall(itemscopy);

	let ui = libtui::init();
	defer libtui::finish(&ui);
	let sz = libtui::getwinsize(ui)!;
	let rows: (u16 | size) = if (sz.rows - 1u16 < len(items)) {
		yield sz.rows - 1u16;
	} else {
		yield len(items);
	};
	let l = list::newlistsz(ui, tty::ttysize {
		rows = rows: u16,
		columns = sz.columns,
	}, items...);
	libtui::addlistener(&ui, &globalrunehandler);
	widget::addlistener(&l, &runehandler);

	let layout = layout::newlayout(&l);
	defer layout::finishall(&layout);

	if (searchterm != "") {
		list::search(&l, searchterm);
	};

	//defer free(searchterm);
	//libtui::clear(l.widget.ui);

	signal::block(signal::sig::WINCH);
	let sfd = match (signal::signalfd(signal::sig::WINCH)) {
	case let sfd: io::file =>
		yield sfd;
	case let e: errors::error =>
		fmt::fatal(errors::strerror(e));
	};
	defer io::close(sfd)!;

	const pollfds: [2]poll::pollfd = [poll::pollfd {
		fd = ui.f,
		events = poll::event::POLLIN,
		...
	}, poll::pollfd {
		fd = sfd,
		events = poll::event::POLLIN,
		...
	}];

	for (true) {
		libtui::doclear(&l.widget.ui);
		match (layout::print(layout)) {
		case void =>
			yield;
		case let e: io::error =>
			fmt::fatal(io::strerror(e));
		case let e: tty::error =>
			fmt::fprintln(os::stderr, tty::strerror(e))!;
			break;
		};
		let nb = match (poll::poll(pollfds, poll::INDEF)) {
		case let nb: uint =>
			yield nb;
		case let e: poll::error =>
			fmt::fprintln(os::stderr, poll::strerror(e))!;
			break;
		};
		let readin = false;
		if (nb == 0) {
			continue;
		} else {
			if (pollfds[0].revents & poll::event::POLLIN != 0) {
				readin = true;
			};
			if (pollfds[1].revents & poll::event::POLLIN != 0) {
				signal::read(pollfds[1].fd)!;
				resize(&l);
			};
		};
		if (readin) {
			let r = match (libtui::scan(ui)) {
			case let r: rune =>
				yield r;
			case utf8::invalid =>
				fmt::fprintln(os::stderr, "Invalid utf8 sequence")!;
				break;
			case io::EOF =>
				fmt::fprintln(os::stderr, "EOF")!;
				break;
			case let e: io::error =>
				fmt::fprintln(os::stderr, io::strerror(e))!;
				break;
			};
			if (libtui::notify(&ui, r)) {
				break;
			};
			if (list::notify(&l, r)) {
				break;
			};
		};
	};
};

@fini fn finish() void = {
	free(searchterm);
};
