#include <stic.h>

#include "../../src/engine/cmds.h"
#include "../../src/engine/completion.h"
#include "../../src/lua/vlua.h"
#include "../../src/ui/statusbar.h"
#include "../../src/ui/ui.h"
#include "../../src/cmd_core.h"
#include "../../src/status.h"

#include <test-utils.h>

#include "asserts.h"

static void check_next_completion(const char expected[]);

static vlua_t *vlua;

SETUP()
{
	vlua = vlua_init();
	curr_stats.vlua = vlua;

	curr_view = &lwin;
	other_view = &rwin;

	engine_cmds_setup(/*real_completion=*/1);
}

TEARDOWN()
{
	vlua_finish(vlua);
	curr_stats.vlua = NULL;

	engine_cmds_teardown();
}

TEST(cmds_add)
{
	cmds_init();

	GLUA_EQ(vlua, "",
			"function handler(info)"
			"  if info.args == nil then"
			"    print 'args is missing'"
			"  end"
			"  if info.argv == nil then"
			"    print 'argsv is missing'"
			"  end"
			"  if info.args ~= info.argv[1] then"
			"    print 'args or argv is wrong'"
			"  end"
			"  print 'msg'"
			"end");
	GLUA_EQ(vlua, "",
			"function badhandler()\n"
			"  adsf()\n"
			"end");

	BLUA_ENDS(vlua, "`handler` key is mandatory",
			"vifm.cmds.add { name = 'cmd' }");
	BLUA_ENDS(vlua, "`name` key is mandatory",
			"vifm.cmds.add { handler = handler }");
	BLUA_ENDS(vlua, "`handler` value must be a function",
			"vifm.cmds.add { name = 'cmd', handler = 10 }");
	BLUA_ENDS(vlua, "`minargs` value must be a number",
			"vifm.cmds.add { name = 'cmd', handler = handler, minargs = 'min' }");
	BLUA_ENDS(vlua, "`maxargs` value must be a number",
			"vifm.cmds.add { name = 'cmd', handler = handler, maxargs = 'max' }");

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'cmd',"
			"  description = 'description',"
			"  handler = handler,"
			"  minargs = 1,"
			"}");
	assert_failure(cmds_dispatch1("cmd arg", curr_view, CIT_COMMAND));
	assert_string_equal("msg", ui_sb_last());
	assert_int_equal(1, curr_stats.save_msg);

	GLUA_EQ(vlua, "",
			"vifm.cmds.add { name = 'bcmd', handler = badhandler, minargs = 0 }");
	assert_failure(cmds_dispatch1("bcmd", curr_view, CIT_COMMAND));
	assert_string_ends_with(": attempt to call a nil value (global 'adsf')",
			ui_sb_last());

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'cmdinf',"
			"  handler = handler,"
			"  minargs = 1,"
			"  maxargs = -1"
			"}");
	assert_failure(cmds_dispatch1("cmdinf arg1 arg2", curr_view, CIT_COMMAND));
	assert_string_equal("msg", ui_sb_last());
	assert_int_equal(1, curr_stats.save_msg);
}

TEST(cmds_command)
{
	BLUA_ENDS(vlua, "Action can't be empty",
			"vifm.cmds.command { name = 'name', action = '' }");

	GLUA_EQ(vlua, "",
			"r = vifm.cmds.command({"
			"  name = 'name',"
			"  action = 'action',"
			"  description = 'descr'"
			"})"
			"if not r then print 'fail' end");

	GLUA_EQ(vlua, "fail",
			"r = vifm.cmds.command { name = 'name', action = 'action' }"
			"if not r then print 'fail' end");

	GLUA_EQ(vlua, "",
			"r = vifm.cmds.command {"
			"  name = 'name',"
			"  action = 'action',"
			"  overwrite = true"
			"}"
			"if not r then print 'fail' end");
}

TEST(cmds_range)
{
	cmds_init();

	curr_view->list_pos = 3;
	curr_view->list_rows = 10;

	GLUA_EQ(vlua, "",
			"r = vifm.cmds.add {"
			"  name = 'cmd',"
			"  withrange = true,"
			"  handler = function(info) handler(info) end,"
			"}"
			"if not r then print 'fail' end");

	GLUA_EQ(vlua, "",
			"function handler(info)"
			"  print(info.range)"
			"end");

	ui_sb_msg("");
	assert_failure(cmds_dispatch1("cmd", curr_view, CIT_COMMAND));
	assert_string_equal("nil", ui_sb_last());

	GLUA_EQ(vlua, "",
			"function handler(info)"
			"  print(info.range.from, info.range.to)"
			"end");

	ui_sb_msg("");
	assert_failure(cmds_dispatch1("1,5cmd", curr_view, CIT_COMMAND));
	assert_string_equal("1\t5", ui_sb_last());
	assert_failure(cmds_dispatch1("1,$cmd", curr_view, CIT_COMMAND));
	assert_string_equal("1\t10", ui_sb_last());
	assert_failure(cmds_dispatch1("1,500cmd", curr_view, CIT_COMMAND));
	assert_string_equal("1\t10", ui_sb_last());
	assert_failure(cmds_dispatch1("2cmd", curr_view, CIT_COMMAND));
	assert_string_equal("2\t2", ui_sb_last());
	assert_failure(cmds_dispatch1(".cmd", curr_view, CIT_COMMAND));
	assert_string_equal("4\t4", ui_sb_last());
}

TEST(cmds_names_with_numbers)
{
	GLUA_EQ(vlua, "",
			"r = vifm.cmds.add {"
			"  name = 'my1',"
			"  handler = function() end"
			"}"
			"if not r then print 'fail' end");

	GLUA_EQ(vlua, "",
			"r = vifm.cmds.command {"
			"  name = 'my2',"
			"  action = 'bla'"
			"}"
			"if not r then print 'fail' end");
}

TEST(cmds_delcommand)
{
	GLUA_EQ(vlua, "",
			"r = vifm.cmds.command {"
			"  name = 'name',"
			"  action = 'action'"
			"}"
			"if not r then print 'fail' end");

	GLUA_EQ(vlua, "",
			"r = vifm.cmds.delcommand('name')"
	    "if not r then print 'fail' end");
}

TEST(cmds_completion)
{
	GLUA_EQ(vlua, "", "function handler() end");
	GLUA_EQ(vlua, "", "function bad() end");
	GLUA_EQ(vlua, "", "function failing() asdfadsf() end");
	GLUA_EQ(vlua, "",
			"function completor(info)"
			"  if info.arg == nil then"
			"    print 'arg m isissing'"
			"  end"
			"  if info.args == nil then"
			"    print 'args is missing'"
			"  end"
			"  if info.argv == nil then"
			"    print 'argsv is missing'"
			"  end"
			"  if info.arg ~= info.args then"
			"    print 'arg or args is wrong'"
			"  end"
			"  if info.arg ~= info.argv[1] then"
			"    print 'arg or argv is wrong'"
			"  end"
			"  return {"
			"    offset = 1,"
			"    matches = {"
			"      'aa',"
			"      {match = 'ab',"
			"       description = 'desc'},"
			"      {description = 'only desc'},"
			"      {match = 'bc'}"
			"    }"
			"  }"
			"end");
	GLUA_EQ(vlua, "",
			"function evilcompletor()\n"
			"  return {"
			"    offset = 0,"
			"    matches = { evilcompletor }"
			"  }"
			"end");

	/* :command without completion. */

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'nocompl',"
			"  handler = handler"
			"}");
	assert_int_equal(7, vle_cmds_complete("nocompl a", NULL));
	assert_int_equal(0, vle_compl_get_count());

	/* :command with working completion. */

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'test',"
			"  handler = handler,"
			"  complete = completor"
			"}");
	vle_compl_reset();
	assert_int_equal(6, vle_cmds_complete("test a", NULL));
	check_next_completion("aa");
	check_next_completion("ab");
	check_next_completion("bc");
	check_next_completion("a");

	/* :command with completion that returns nil. */

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'testcmd',"
			"  handler = handler,"
			"  complete = bad"
			"}");
	vle_compl_reset();
	assert_int_equal(8, vle_cmds_complete("testcmd p", NULL));
	assert_int_equal(0, vle_compl_get_count());

	/* :command with completion that errors. */

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'tcmd',"
			"  handler = handler,"
			"  complete = failing"
			"}");
	vle_compl_reset();
	assert_int_equal(5, vle_cmds_complete("tcmd z", NULL));
	assert_int_equal(0, vle_compl_get_count());

	/* :command with completion that has unsupported values. */

	GLUA_EQ(vlua, "",
			"vifm.cmds.add {"
			"  name = 'evilcmpl',"
			"  handler = handler,"
			"  complete = evilcompletor"
			"}");
	vle_compl_reset();
	assert_int_equal(9, vle_cmds_complete("evilcmpl z", NULL));
	assert_int_equal(1, vle_compl_get_count());
}

static void
check_next_completion(const char expected[])
{
	char *buf = vle_compl_next();
	assert_string_equal(expected, buf);
	free(buf);
}

/* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
/* vim: set cinoptions+=t0 : */
