/*-
 * Copyright (c) 1998 fjoe <fjoe@iclub.nsu.ru>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: olc_rule.c,v 1.8 1999/04/17 06:56:39 fjoe Exp $
 */

#include <stdio.h>
#include <stdlib.h>

#include "merc.h"
#include "olc.h"
#include "db/lang.h"

#define EDIT_RULE(ch, r)	(r = (rule_t*) ch->desc->pEdit)
#define EDIT_RCL(ch, rcl)	(rcl = (rulecl_t*) ch->desc->pEdit2)
#define EDIT_ROPS(ch, rops)	(rops = (ruleops_t*) cmd->arg1)
#define EDIT_LANG(ch, l)						\
	{								\
		if ((l = varr_get(&langs, ch->lang)) == NULL) {		\
			char_puts("RuleEd: unknown current language. "	\
				  "Use 'lang' command to set "		\
				  "correct language.\n", ch);		\
			return FALSE;					\
		}							\
	}

typedef struct ruleops_t ruleops_t;
struct ruleops_t {
	rule_t*		(*rule_lookup)(rulecl_t *rcl, const char *name);
	void		(*rule_del)(rulecl_t *rcl, rule_t *r);
	const char *	id;
	flag32_t	bit;
};

ruleops_t rops_expl = { erule_lookup, erule_del, ED_EXPL, RULES_EXPL_CHANGED };
ruleops_t rops_impl = { irule_lookup, irule_del, ED_IMPL, RULES_IMPL_CHANGED };

DECLARE_OLC_FUN(ruleed_create	);
DECLARE_OLC_FUN(ruleed_edit	);
DECLARE_OLC_FUN(ruleed_touch	);
DECLARE_OLC_FUN(ruleed_show	);
DECLARE_OLC_FUN(ruleed_list	);

DECLARE_OLC_FUN(ruleed_name	);
DECLARE_OLC_FUN(ruleed_base	);
DECLARE_OLC_FUN(ruleed_arg	);
DECLARE_OLC_FUN(ruleed_add	);
DECLARE_OLC_FUN(ruleed_del	);
DECLARE_OLC_FUN(ruleed_delete	);

DECLARE_OLC_FUN(eruleed_name	);
DECLARE_OLC_FUN(iruleed_name	);
DECLARE_OLC_FUN(eruleed_list	);

olc_cmd_t olc_cmds_expl[] =
{
	{ "create",		ruleed_create,	&rops_expl	},
	{ "edit",		ruleed_edit,	&rops_expl	},
	{ "touch",		ruleed_touch,	&rops_expl	},
	{ "show",		ruleed_show,	&rops_expl	},
	{ "list",		ruleed_list,	&rops_expl	},

	{ "name",		eruleed_name			},
	{ "base",		ruleed_base			},
	{ "add",		ruleed_add			},
	{ "del",		ruleed_del			},
	{ "rule_delet",		olced_spell_out			},
	{ "rule_delete",	ruleed_delete,	&rops_expl	},

	{ "commands",		show_commands			},
	{ NULL }
};

olc_cmd_t olc_cmds_impl[] =
{
	{ "create",		ruleed_create,	&rops_impl	},
	{ "edit",		ruleed_edit,	&rops_impl	},
	{ "touch",		ruleed_touch,	&rops_impl	},
	{ "show",		ruleed_show,	&rops_impl	},
	{ "list",		ruleed_list,	&rops_impl	},

	{ "name",		iruleed_name			},
	{ "arg",		ruleed_arg			},
	{ "add",		ruleed_add			},
	{ "del",		ruleed_del			},
	{ "delete_rul",		olced_spell_out			},
	{ "delete_rule",	ruleed_delete,	&rops_impl	},

	{ "commands",		show_commands			},
	{ NULL }
};

OLC_FUN(ruleed_create)
{
	int rulecl;
	rulecl_t *rcl;
	ruleops_t *rops;
	lang_t *l;
	rule_t rnew;
	char arg[MAX_STRING_LENGTH];
	char arg2[MAX_STRING_LENGTH];
	bool impl;

	if (ch->pcdata->security < SECURITY_MSGDB) {
		char_puts("RuleEd: Insufficient security.\n", ch);
		return FALSE;
	}

	EDIT_LANG(ch, l);
	EDIT_ROPS(ch, rops);
	impl = (rops->id == ED_IMPL);

	argument = one_argument(argument, arg, sizeof(arg));
	if (impl)
		argument = one_argument(argument, arg2, sizeof(arg2));
	if (argument[0] == '\0' || (impl && !is_number(arg2))) {
		do_help(ch, "'OLC CREATE'");
		return FALSE;
	}

	if ((rulecl = flag_value(rulecl_names, arg)) < 0) {
		char_printf(ch, "RuleEd: %s: unknown rule class.\n", arg);
		return FALSE;
	}
	rcl = l->rules + rulecl;

	if (!impl && erule_lookup(rcl, argument)) {
		char_printf(ch, "RuleEd: %s: duplicate name.\n", argument);
		return FALSE;
	}

	if (olced_busy(ch, rops->id, NULL, rcl))
		return FALSE;

	rule_init(&rnew);
	rnew.name = str_dup(argument);
	OLCED(ch)	= olced_lookup(rops->id);
	ch->desc->pEdit = impl ? irule_insert(rcl, atoi(arg2), &rnew) :
				 erule_add(rcl, &rnew);
	ch->desc->pEdit2= rcl; 
	SET_BIT(rcl->flags, rops->bit);
	char_puts("RuleEd: rule created.\n", ch);
	return FALSE;
}

OLC_FUN(ruleed_edit)
{
	int rulecl;
	rulecl_t *rcl;
	ruleops_t *rops;
	lang_t *l;
	rule_t *r;
	char arg[MAX_INPUT_LENGTH];

	if (ch->pcdata->security < SECURITY_MSGDB) {
		char_puts("RuleEd: Insufficient security.\n", ch);
		return FALSE;
	}

	argument = one_argument(argument, arg, sizeof(arg));
	if (argument[0] == '\0') {
		do_help(ch, "'OLC EDIT'");
		return FALSE;
	}

	EDIT_LANG(ch, l);
	EDIT_ROPS(ch, rops);

	if ((rulecl = flag_value(rulecl_names, arg)) < 0) {
		char_printf(ch, "RuleEd: %s: unknown rule class.\n", arg);
		return FALSE;
	}
	rcl = l->rules+rulecl;

	if ((r = rops->rule_lookup(rcl, argument)) == NULL) {
		char_printf(ch, "RuleEd: %s: not found.\n", argument);
		return FALSE;
	}

	ch->desc->olced = olced_lookup(rops->id);
	ch->desc->pEdit	= r;
	ch->desc->pEdit2= rcl;
	return FALSE;
}

OLC_FUN(ruleed_touch)
{
	rulecl_t *rcl;
	ruleops_t *rops;

	EDIT_RCL(ch, rcl);
	EDIT_ROPS(ch, rops);
	SET_BIT(rcl->flags, rops->bit);
	return FALSE;
}

OLC_FUN(ruleed_show)
{
	int i;
	rule_t *r;
	rulecl_t *rcl;
	ruleops_t *rops;
	lang_t *l;

	EDIT_LANG(ch, l);
	EDIT_ROPS(ch, rops);

	if (argument[0] == '\0') {
		if (IS_EDIT(ch, ED_IMPL) || IS_EDIT(ch, ED_EXPL)) {
			EDIT_RULE(ch, r);
			EDIT_RCL(ch, rcl);
		}
		else {
			do_help(ch, "'OLC ASHOW'");
			return FALSE;
		}
	}
	else {
		int rulecl;
		char arg[MAX_INPUT_LENGTH];

		argument = one_argument(argument, arg, sizeof(arg));
		if (argument[0] == '\0') {
			do_help(ch, "'OLC ASHOW'");
			return FALSE;
		}

		if ((rulecl = flag_value(rulecl_names, arg)) < 0) {
			char_printf(ch, "RuleEd: %s: unknown rule class.\n",
				    arg);
			return FALSE;
		}
		rcl = l->rules + rulecl;

		if ((r = rops->rule_lookup(rcl, argument)) == NULL) {
			char_printf(ch, "RuleEd: %s: not found.\n", argument);
			return FALSE;
		}
	}

	char_printf(ch, "Name: [%s]\n"
			"Lang: [%s]  Class: [%s]  Type: [%s]\n",
		    r->name,
		    l->name, flag_string(rulecl_names, rcl->rulecl),
		    rops->id);

	if (rops->id == ED_IMPL) 
		char_printf(ch, "Arg:  [%d]\n", r->arg);
	else {
		char buf[MAX_STRING_LENGTH];

		strnzncpy(buf, sizeof(buf), r->name, r->arg);
		char_printf(ch, "Base: [%s] (%d)\n", buf, r->arg);
	}

	for (i = 0; i < r->f->v.nused; i++) {
		char **p = VARR_GET(&r->f->v, i);

		if (!IS_NULLSTR(*p))
			char_printf(ch, "Form: [%d] [%s]\n", i, *p);
	}

	return FALSE;
}

OLC_FUN(ruleed_list)
{
	int i;
	rule_t *r;
	rulecl_t *rcl;
	ruleops_t *rops;
	lang_t *l;
	BUFFER *output = NULL;

	EDIT_LANG(ch, l);
	EDIT_ROPS(ch, rops);

	if (IS_EDIT(ch, ED_IMPL) || IS_EDIT(ch, ED_EXPL)) {
		EDIT_RULE(ch, r);
		EDIT_RCL(ch, rcl);
	}
	else {
		int rulecl;
		char arg[MAX_INPUT_LENGTH];

		argument = one_argument(argument, arg, sizeof(arg));
		if (arg[0] == '\0') {
			do_help(ch, "'OLC ALIST'");
			return FALSE;
		}

		if ((rulecl = flag_value(rulecl_names, arg)) < 0) {
			char_printf(ch, "RuleEd: %s: unknown rule class.\n",
				    arg);
			return FALSE;
		}
		rcl = l->rules + rulecl;
	}

	if (rops->id == ED_IMPL) {
		if (rcl->impl.nused) {
			output = buf_new(-1);
			for (i = 0; i < rcl->impl.nused; i++) {
				rule_t *r = VARR_GET(&rcl->impl, i);
				buf_printf(output, "%3d. %s\n", i, r->name);
			}
		}
	}
	else {
		if (argument[0] == '\0') {
			do_help(ch, "'OLC ALIST'");
			return FALSE;
		}
	
		for (i = 0; i < MAX_RULE_HASH; i++) {
			int j;

			for (j = 0; j < rcl->expl[i].nused; j++) {
				rule_t *r = VARR_GET(rcl->expl+i, j);

				if (!str_prefix(argument, r->name)) {
					if (!output)
						output = buf_new(-1);
					buf_printf(output, "%s\n", r->name);
				}
			}
		}
	}

	if (output) {
		page_to_char(buf_string(output), ch);
		buf_free(output);
	}
	else
		char_puts("RuleEd: no rules found.\n", ch);

	return FALSE;
}

OLC_FUN(eruleed_name)
{
	rule_t *r;
	rule_t *r2;
	rule_t rnew;
	rulecl_t *rcl;

	if (argument[0] == '\0') {
		do_help(ch, "'OLC RULE'");
		return FALSE;
	}

	EDIT_RULE(ch, r);
	EDIT_RCL(ch, rcl);
	if (olced_busy(ch, ED_EXPL, NULL, rcl))
		return FALSE;

	if ((r2 = erule_lookup(rcl, argument)) && r2 != r) {
		char_printf(ch, "RuleEd: %s: duplicate name.\n", argument);
		return FALSE;
	}

	erule_del(rcl, r);
	rule_init(&rnew);
	rnew.name = str_dup(argument);
	ch->desc->pEdit = erule_add(rcl, &rnew);
	return TRUE;
}

OLC_FUN(iruleed_name)
{
	rule_t *r;
	EDIT_RULE(ch, r);
	return olced_str(ch, argument, cmd, &r->name);
}

OLC_FUN(ruleed_base)
{
	char arg[MAX_INPUT_LENGTH];
	rule_t *r;
	EDIT_RULE(ch, r);

	one_argument(argument, arg, sizeof(arg));
	if (str_prefix(arg, r->name)) {
		char_printf(ch, "RuleEd: %s: not prefix of name (%s).\n",
			    arg, r->name);
		return FALSE;
	}

	r->arg = strlen(arg);
	return TRUE;
}

OLC_FUN(ruleed_arg)
{
	rule_t *r;
	EDIT_RULE(ch, r);
	return olced_number(ch, argument, cmd, &r->arg);
}

OLC_FUN(ruleed_add)
{
	rule_t *r;
	EDIT_RULE(ch, r);
	return olced_vform_add(ch, argument, cmd, r);
}

OLC_FUN(ruleed_del)
{
	rule_t *r;
	EDIT_RULE(ch, r);
	return olced_vform_del(ch, argument, cmd, r);
}

OLC_FUN(ruleed_delete)
{
	rule_t *r;
	rulecl_t *rcl;
	ruleops_t *rops;

	EDIT_RULE(ch, r);
	EDIT_RCL(ch, rcl);
	EDIT_ROPS(ch, rops);

	if (olced_busy(ch, rops->id, NULL, rcl))
		return FALSE;

	rops->rule_del(rcl, r);
	SET_BIT(rcl->flags, rops->bit);
	edit_done(ch->desc);

	return FALSE;
}

