/*
  Security from sites and bad names as well as
  crashproof saving of players items. Contributions
  by Taquin Ho ( prometheus ) and Jeff Stile ( Abaddon )
*/

#include <strings.h>
#include <stdio.h>
#include <ctype.h>

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "db.h"

extern struct index_data *obj_index;

/*****************************************************************
*  CRASHSAFE                                                     *
*  These functions handle saving inventory in case of a crash    *
*  You must create a directory in the lib directory called crash *
*****************************************************************/

/****************************************************************
*    call here from handler.c: extract_char			*
****************************************************************/
void update_crash(struct char_data *ch)
{
	char fname[MAX_STRING_LENGTH];
	FILE *fl;

	sprintf(fname, "crash/%s.crash", ch->player.name);
	if (!(fl = fopen(fname, "rb")))
		return;
	fclose(fl);
	/* Pointed out by MERC that the old system() call was expensive */
	unlink(fname);
}

void obj2_store_to_char(struct char_data *ch, struct obj_file_elem *object)
{
	struct obj_data *obj;
	int j;

	void obj_to_char(struct obj_data *object, struct char_data *ch);
	if (real_object(object->item_number) > -1) 
	{
		obj = read_object(object->item_number, VIRTUAL);
		obj->obj_flags.value[0] = object->value[0];
		obj->obj_flags.value[1] = object->value[1];
		obj->obj_flags.value[2] = object->value[2];
		obj->obj_flags.value[3] = object->value[3];

		obj->obj_flags.extra_flags = object->extra_flags;
		obj->obj_flags.weight      = object->weight;
		obj->obj_flags.timer       = object->timer;
		obj->obj_flags.bitvector   = object->bitvector;

		for(j=0; j<MAX_OBJ_AFFECT; j++)
			obj->affected[j] = object->affected[j];

		obj_to_char(obj, ch);
	}
}


/*******************************************************************
*	call here from interpreter when loading character	   *
*******************************************************************/
void load_char_objs2(struct char_data *ch)
{
	FILE *fl;
	char fname[MAX_STRING_LENGTH];
	struct obj_file_elem object;


	sprintf(fname, "oldcrash/%s.crash", ch->player.name);
	if (!(fl = fopen(fname, "rb"))) {
		return;
	}
	log("Loading char equipment from crash");
	while (!feof(fl)) {
		fread(&object, sizeof(object), 1, fl);
			if (ferror(fl)) {
				perror("reading player file -load_char_objs2");
				fclose(fl);
				return;
			}
		if (!feof(fl))
			obj2_store_to_char(ch, &object);
	}
		
	fclose(fl);

	sprintf(fname, "rm -f oldcrash/%s.crash", ch->player.name);
	system(fname);	
}

/*
 * Assumes file is open with write privilege
 */
bool put_obj2_in_store(struct obj_data *obj, struct char_data *ch, FILE *fl)
{
	int j;
	char fname[MAX_STRING_LENGTH];
	struct obj_file_elem object;

	object.item_number = obj_index[obj->item_number].virtual;
	object.value[0] = obj->obj_flags.value[0];
	object.value[1] = obj->obj_flags.value[1];
	object.value[2] = obj->obj_flags.value[2];
	object.value[3] = obj->obj_flags.value[3];
	object.extra_flags = obj->obj_flags.extra_flags;
	object.weight  = obj->obj_flags.weight;
	object.timer  = obj->obj_flags.timer;
	object.bitvector  = obj->obj_flags.bitvector;
	for(j=0; j<MAX_OBJ_AFFECT; j++)
		object.affected[j] = obj->affected[j];
	
	if (fwrite(&object, sizeof(object), 1, fl) < 1) {
		perror("writing crash data -put_obj2_in_store");
		return FALSE;
	}
	return TRUE;
}

bool obj2_to_store(struct obj_data *obj, struct char_data *ch, FILE *fp)
{
	struct obj_data *tmp;
	bool result;

	if (obj) {
		obj2_to_store(obj->contains, ch, fp);
		obj2_to_store(obj->next_content, ch, fp);

		result = put_obj2_in_store(obj, ch, fp);
		if (!result) {
			return FALSE;
		} /* if */

		for (tmp=obj->in_obj;tmp;tmp=tmp->in_obj)
			GET_OBJ_WEIGHT(tmp) -= GET_OBJ_WEIGHT(obj);
	}
	return TRUE;
}

/**************************************************************
*  required to keep total weight of containers from changing  *
**************************************************************/
void restore_weight(struct obj_data *obj)
{
	struct obj_data *tmp;

	if (obj) {
		/* These two lines were after the recursion, which would */
		/* double-count items going up the chain. -Sman          */
		for (tmp=obj->in_obj;tmp;tmp=tmp->in_obj)
			GET_OBJ_WEIGHT(tmp) += GET_OBJ_WEIGHT(obj);
		restore_weight(obj->contains);
		restore_weight(obj->next_content);
	}
}


/************************************************************************
*	call here from : do_save, and after loading character 		*
*	to save the characters inventory				*
*									*
*       can also call anytime inventory changes ie:			*
*       do_get, do_drop, buy, sell, steal etc.				*
*									*
************************************************************************/

void save_obj2(struct char_data *ch)
{
	int j;
	char buf[MAX_STRING_LENGTH];
	FILE *fp;
	
	if (IS_NPC(ch))
		return;

	sprintf(buf, "crash/%s.crash", ch->player.name);
	if (!(fp = fopen(buf, "wb")))
		return;

	if (!obj2_to_store(ch->carrying, ch, fp)) {
		fclose(fp);
		return;
	} /* if */

	restore_weight(ch->carrying); 
	for(j=0; j<MAX_WEAR; j++)
		if (ch->equipment[j]) {
			if (!obj2_to_store(ch->equipment[j], ch, fp)) {
				fclose(fp);
				return;
			}
			restore_weight(ch->equipment[j]); 
		}

	fclose(fp);
}



/***************************************************************************
*	In comm.c within the game_loop add:
*
*	if (pulse2 == 3) {      
*     /comment 3 = 1/2 hr  each +1 increases delay 10min comment/
*		system("rm crash/*.crash");
*     /comment or as used in alex : system("mv crash/* crash/oldcrash"); com/
*		for (point = descriptor_list;point;point = next_point)
*		{
*                 next_point = point->next;
*                 if (!IS_NPC(ch))
*		  save_obj2(ch);
*               }
*		pulse2 = -1;
*	}
*
*	if (pulse >= 2400) {
*		if (pulse2 >= 0)
*			pulse2++;
*		pulse = 0
*		.
*		.
*		.
*
*      This will remove all save files so that a person must log back on
*      within alotted time after a crash to retrieve items.
*      (Ammendment made by Taquin : addition of point, next_point
*                                   or else it does not work....)
****************************************************************************/