/*
 * prsync.c
 * 
 * Subroutines used to print C_SYNC messages
 *
 * Ken Stevens, 1995
 */

#include <ctype.h>
#include "misc.h"
#include "player.h"
#include "file.h"
#include "edb.h"
#include "xy.h"
#include "nsc.h"
#include "genitem.h"
#include "optlist.h"
#include "map.h"

static u_short decodechr();
static s_char *replace_colon();

pr_field_defs(ca, end_at)
	struct	castr	*ca;
	long	end_at;
{
	for (; ca->ca_code; ++ca) {
		if (ca->ca_code & NSC_DEITY && !player->god)
			continue;
		pr_sync("%s,", ca->ca_name);
		pr_ca(ca);
		if (ca->ca_len)
			pr_sync(",%u", ca->ca_len);
		pr_sync(":");
		if (end_at && ca->ca_edv == end_at)
			break;
	}
}

static
pr_ca(ca)
	struct	castr	*ca;
{
	s_char	edb_type;
	
	if (ca->ca_code & NSC_VAR)
		edb_type = EDB_USHORT;
	else switch(ca->ca_code & NSC_TMASK) {
	case NSC_CHAR:
	case NSC_CHARP:
		edb_type = EDB_CHAR; break;
	case NSC_UCHAR:
		edb_type = EDB_UCHAR; break;
	case NSC_SHORT:
	case NSC_XCOORD:
	case NSC_YCOORD:
		edb_type = EDB_SHORT; break;
	case NSC_USHORT:
		edb_type = EDB_USHORT; break;
	case NSC_INT:
		edb_type = EDB_INT; break;
	case NSC_LONG:
		edb_type = EDB_LONG; break;
	case NSC_FLOAT:
		edb_type = EDB_FLOAT; break;
	default:
		edb_type = EDB_BAD;
	}
	pr_sync("%u", edb_type);
}

prsync(cnum, ob, edv)
	natid	cnum;
	struct	genobject *ob;
	long	edv;
{
	struct	castr	*ca;
	struct	edbstr	*ed = &edbchr[ob->ef_type];

	if (!(ca = ed->edb_of))
		return;

	pr_async(cnum, "%d ", ed->edb_object);
	pr_vals(cnum, 0, ob, ca, 0L, edv);
	if (ed->edb_last_var)
		pr_vals(cnum, 0, ob, var_ca, ed->edb_last_var, edv);
	pr_async(cnum, "\n");
}

pr_chr(ed)
	struct	edbstr	*ed;
{
	int	i;
	s_char	*ptr = ed->edb_chr;

	for (i = 0; i < *ed->edb_chrno; ++i, ptr += ed->edb_chrsize) {
		pr_sync("%d ", ed->edb_class);
		pr_vals(player->cnum, 1, ptr, ed->edb_cf, 0, 0);
		if (ed->edb_last_com)
			pr_vals(player->cnum, 1, ptr, var_ca, ed->edb_last_com, 0);
		pr_sync("\n");
	}
}

pr_vals(cnum, isclass, ob, ca, end_at, edv)
	natid	cnum;
	int	isclass;
        struct  genobject *ob;
        struct  castr   *ca;
	long	end_at;
        long    edv;
{
	for (;ca->ca_code; ++ca) {
		if (ca->ca_code & NSC_DEITY && !player->god)
			continue;
		if (!edv || ca->ca_edv & edv || ca->ca_edv == EDV_ID) {
			if (isclass && ca->ca_code & NSC_VAR)
				pr_sync("%u",decodechr(ob, ca));
			else
				pr_ca_val(cnum, ob, ca);
		}
		pr_async(cnum,":");
		if (edv && ca->ca_edv > edv)
			break;
		if (end_at && ca->ca_edv == end_at)
			break;
	}
}

static
pr_ca_val(cnum, ob, ca)
	natid	cnum;
	struct	genobject *ob;
	struct	castr *ca;
{
	u_char	*u;
	short	*h;
	u_short *us;
	int	*in;
	long	*l;
	float	*f;
	int	i;
	long	code = ca->ca_code;

	if (ob->own == cnum)
		code &= ~NSC_ROUND;

	if (code & NSC_VAR) {
		pr_async(cnum, "%u", decode(cnum, code, ob, ob->ef_type));
		return;
	}
	switch(code & NSC_TMASK) {
	case NSC_CHAR:
	case NSC_CHARP:
		if (ca->ca_len)
			pr_async(cnum, "%s", replace_colon(decodep(code, ob)));
		else
			pr_async(cnum, "%d", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_XCOORD:
	case NSC_YCOORD:
		pr_async(cnum, "%hd", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_UCHAR:
		if (ca->ca_len) {
			u = (u_char *)decodep(code, ob);
			for (i = 0; !i || i < ca->ca_len; ++i, ++u)
				pr_async(cnum, "%s%u", i?",":"", *u);
		} else
			pr_async(cnum, "%u", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_SHORT:
		if (ca->ca_len) {
			h = (short *)decodep(code, ob);
			for (i = 0; !i || i < ca->ca_len; ++i, ++h)
				pr_async(cnum, "%s%hd", i?",":"", *h);
		} else
			pr_async(cnum, "%hd", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_USHORT:
		if (ca->ca_len) {
			us = (u_short *)decodep(code, ob);
			for (i = 0; !i || i < ca->ca_len; ++i, ++us)
				pr_async(cnum, "%s%hu", i?",":"", *us);
		} else
			pr_async(cnum, "%hu", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_INT:
		if (ca->ca_len) {
			in = (int *)decodep(code, ob);
			for (i = 0; !i || i < ca->ca_len; ++i, ++in)
				pr_async(cnum, "%s%d", i?",":"", *in);
		} else
			pr_async(cnum, "%d", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_LONG:
		if (ca->ca_len) {
			l = (long *)decodep(code, ob);
			for (i = 0; !i || i < ca->ca_len; ++i, ++l)
				pr_async(cnum, "%s%ld", i?",":"", *l);
		} else
			pr_async(cnum, "%ld", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	case NSC_FLOAT:
		if (ca->ca_len) {
			f = (float *)decodep(code, ob);
			for (i = 0; !i || i < ca->ca_len; ++i, ++f)
				pr_async(cnum, "%s%f", i?",":"", *f);
		} else
			pr_async(cnum, "%f", decode(cnum, code|NSC_OFF, ob, ob->ef_type));
		break;
	default:
		pr_async(cnum, "ERROR");
	}	
}

static u_short
decodechr(cp, ca)
	struct	genchrstr	*cp;
	struct	castr	*ca;
{
	int	vtype = (ca->ca_code & ~NSC_MASK) & 0xffff;

	return vl_find(vtype, cp->vtype, cp->vamt, cp->nv);
}

pr_ver_defs()
{
	extern	struct keymatch	configkeys[];
	extern	struct option_list Options[];
	struct	keymatch *km = configkeys;
	struct	option_list *ol = Options;
	int	edb_type, edb_len;

	pr_sync("%d %d %d ",
		EDB_TYPE_DEF,
		EDB_CLASS,
		VER_CLASS);
	pr_sync("MAXNOC,%u:", EDB_INT);
	pr_sync("WORLD_X,%u:", EDB_INT);
	pr_sync("WORLD_Y,%u:", EDB_INT);
	for (; km->km_key; ++km) {
		edb_len = 0;
		if (km->km_func == strset) {
			edb_type = EDB_CHAR;
			edb_len = 80;
		} else if (km->km_func == floatset)
			edb_type = EDB_FLOAT;
		else if (km->km_func == intset)
			edb_type = EDB_INT;
		else if (km->km_func == doubleset)
			edb_type = EDB_DOUBLE;
		else if (km->km_func == longset)
			edb_type = EDB_LONG;
		else
			continue;

		pr_sync("%s,", km->km_key);
		pr_sync("%u", edb_type);
		if (edb_len)
			pr_sync(",%u", edb_len);
		pr_sync(":");
	}

	for (;ol->opt_key; ++ol) {
		pr_sync("%s,%u:", ol->opt_key, EDB_CHAR);
	}
	pr_sync("\n");
}

pr_ver()
{
	extern	struct keymatch	configkeys[];
	extern	struct option_list Options[];
	struct	keymatch *km = configkeys;
	struct	option_list *ol = Options;

	pr_sync("%d ", VER_CLASS);
	pr_sync("%d:", MAXNOC);
	pr_sync("%d:", WORLD_X);
	pr_sync("%d:", WORLD_Y);
	for (; km->km_key; ++km) {
		if (km->km_func == strset) {
			pr_sync("%.80s", replace_colon(*(s_char **)km->km_data));
		} else if (km->km_func == floatset)
			pr_sync("%f", *(float *)km->km_data);
		else if (km->km_func == intset)
			pr_sync("%d", *(int *)km->km_data);
		else if (km->km_func == doubleset)
			pr_sync("%g", *(double *)km->km_data);
		else if (km->km_func == longset)
			pr_sync("%ld", *(long *)km->km_data);
		else
			continue;

		pr_sync(":");
	}

	for (;ol->opt_key; ++ol) {
		pr_sync("%d:", *ol->opt_valuep);
	}
	pr_sync("\n");
}

static s_char *
replace_colon(s)
	s_char	*s;
{
	s_char	*tmp;
	s_char	format[128];

	if (strchr(s,':')) {
		tmp = prbuf("%s", s);
		s = tmp;
		while (*s) {
			if (*s == ':')
				*s = '^';
			++s;
		}
		return tmp;
	}
	return s;
}

prewrite_sync(oldob, newob)
	struct	genitem *oldob;
	struct	genitem *newob;
{
	if (memcmp(oldob, newob, empfile[newob->ef_type].size)) {
		if (newob->own)
			prsync(newob->own, newob, EDV_ALL);
		if (oldob->own != newob->own && oldob->own)
			prsync(oldob->own, newob, EDV_OWN);
	}
}

pr_mapstr(cnum, x, y, des)
	natid	cnum;
	coord	x;
	coord	y;
	s_char	des;
{
	struct	mapstr	ms;

	ms.ef_type = EF_MAP;
	ms.map_x = x;
	ms.map_y = y;
	ms.map_des = des;
	prsync(cnum, &ms, EDV_ALL);
}
