#include "include.h"
#include "tables.h"
#include "magic.h"
#include "lookup.h"
#include "olc.h"
#include "recycle.h"

extern bool		fBootDb;

char *			gsn_name( sh_int *pgsn );
char *			spell_name( SPELL_FUN *spell );
SPELL_FUN *		spell_function( char *argument );
sh_int *		gsn_lookup( char *argument );
#define SKEDIT( fun )	bool fun( CHAR_DATA *ch, char *argument )

#if !defined(FIRST_BOOT)
struct	skill_type	*skill_table;
int			MAX_SKILL;
#endif
struct	skhash		*skill_hash_table[26];

void			crear_tabla_hash_skills		( void );
void			borrar_tabla_hash_skills	( void );

#define	SKILL_FILE	DATA_DIR "skills"

struct	gsn_type
{
	char *		name;
	sh_int *	pgsn;
};

struct	spell_type
{
	char *		name;
	SPELL_FUN *	spell;
};

#if defined(SPELL)
#undef SPELL
#endif
#define SPELL(spell)	{	#spell,		spell		},

const	struct	spell_type	spell_table	[]	=
{
#include "magic.h"
	{	NULL,	NULL	}
};

#undef SPELL
#define SPELL(spell) DECLARE_SPELL_FUN(spell);

#define GSN(x)	{	#x,	&x	},

const	struct	gsn_type	gsn_table	[]	=
{
#include "gsn.h"
	{	NULL,			NULL			}
};

extern struct skill_type xSkill;

const	struct	olc_comm_type	skill_olc_comm_table	[]	=
{
#if !defined(FIRST_BOOT)
 { "name",	NULL,				ed_olded,		skedit_name	},
 { "beats",	(void *) &xSkill.beats,		ed_numero_s_pos,	NULL		},
 { "position",	(void *) &xSkill.minimum_position,	ed_shintlookup,		position_lookup	},
 { "slot",	NULL,				ed_olded,		skedit_slot	},
 { "target",	(void *) &xSkill.target,	ed_flag_set_sh,		target_table	},
 { "mana",	(void *) &xSkill.min_mana,	ed_numero_s_pos,	NULL		},
 { "level",	NULL,				ed_olded,		skedit_nivel 	},
 { "rating",	NULL,				ed_olded,		skedit_rating   },
 { "gsn",	NULL,				ed_olded,		skedit_gsn	},
 { "spell",	NULL,				ed_olded,		skedit_spell	},
 { "noun",	(void *) &xSkill.noun_damage,	ed_line_string,		NULL		},
 { "off",	(void *) &xSkill.msg_off,	ed_line_string,		NULL 		},
 { "obj",	(void *) &xSkill.msg_obj,	ed_line_string,		NULL		},
 { "new",	NULL,				ed_olded,		skedit_new	},
#endif
 { "list",	NULL,				ed_olded,		skedit_list	},
 { "show",	NULL,				ed_olded,		skedit_show	},
 { "commands",	NULL,				ed_olded,		show_commands	},
 { "?",		NULL,				ed_olded,		show_help	},
 { "version",	NULL,				ed_olded,		show_version	},
 { NULL,	NULL,				NULL,			NULL		}
};

char *spell_name( SPELL_FUN *spell )
{
	int i = 0;

	if ( spell == NULL )
		return "";

	while ( spell_table[i].name )
		if ( spell_table[i].spell == spell )
			return spell_table[i].name;
		else
			i++;

	if ( fBootDb )
		bug( "spell_name : spell_fun does not exist", 0 );

	return "";
}

char *gsn_name( sh_int *pgsn )
{
	int i = 0;

	if ( pgsn == NULL )
		return "";

	while ( gsn_table[i].name )
		if ( gsn_table[i].pgsn == pgsn )
			return gsn_table[i].name;
		else
			i++;

	if ( fBootDb )
		bug( "gsn_name : pgsn %d does not exist", *pgsn );

	return "";
}

SPELL_FUN *spell_function( char *argument )
{
	int i;
	char buf[MSL];

	if ( IS_NULLSTR(argument) )
		return NULL;

	for ( i = 0; spell_table[i].name; ++i )
		if ( !str_cmp(spell_table[i].name, argument) )
			return spell_table[i].spell;

	if ( fBootDb )
	{
		sprintf( buf, "spell_function : spell %s does not exist", argument );
		bug( buf, 0 );
	}

	return spell_null;
}

void	check_gsns( void )
{
	int i;

	for ( i = 0; gsn_table[i].name; ++i )
		if ( *gsn_table[i].pgsn == 0 )
			bugf( "check_gsns : gsn %d(%s) not assigned!", i,
				gsn_table[i].name );

	return;
}

sh_int *gsn_lookup( char *argument )
{
	int i;
	char buf[MSL];

	if ( IS_NULLSTR(argument) )
		return NULL;

	for ( i = 0; gsn_table[i].name; ++i )
		if ( !str_cmp(gsn_table[i].name, argument) )
			return gsn_table[i].pgsn;

	if ( fBootDb == TRUE )
	{
		sprintf( buf, "gsn_lookup : gsn %s does nto exist", argument );
		bug( buf, 0 );
	}

	return NULL;
}

void    skedit (CHAR_DATA * ch, char *argument)
{
	if (ch->pcdata->security < MIN_SKEDIT_SECURITY)
	{
		send_to_char ("SKEdit : Security insufficient to edit skill.\n\r", ch);
		edit_done (ch);
		return;
	}

	if (!str_cmp (argument, "done"))
	{
		edit_done (ch);
		return;
	}

	if (!str_cmp (argument, "save"))
	{
		grabar_skills();
		return;
	}

	if ( emptystring(argument) )
	{
		skedit_show (ch, argument);
		return;
	}

	/* Search Table and Dispatch Command. */
	if ( !procesar_comando_olc(ch, argument, skill_olc_comm_table) )
		interpret(ch, argument);

	return;
}

void do_skedit(CHAR_DATA *ch, char *argument)
{
    struct skill_type *pSkill;
    char command[MSL];
    int skill;

    if ( IS_NPC(ch) )
    	return;

    if ( IS_NULLSTR(argument) )
    {
    	send_to_char( "Syntax : SKEdit [skill]\n\r", ch );
    	return;
    }

    if (ch->pcdata->security < MIN_SKEDIT_SECURITY)
    {
    	send_to_char( "SKEdit : Security insufficient to edit skill.\n\r", ch );
    	return;
    }

    one_argument( argument, command );

#if !defined(FIRST_BOOT)
    if ( !str_cmp( command, "new" ) )
    {
	argument = one_argument( argument, command );
	if ( skedit_new(ch, argument) )
		grabar_skills();
	return;
    }
#endif

    if ( (skill = skill_lookup(argument)) == -1 )
    {
    	send_to_char( "SKEdit : Skill does not exist.\n\r", ch );
    	return;
    }

    pSkill = &skill_table[skill];

    ch->desc->pEdit=(void *)pSkill;
    ch->desc->editor= ED_SKILL;

    return;
}

void skill_list( BUFFER *pBuf )
{
	char buf[MSL];
	int i;

	sprintf( buf, "Niv   %-20.20s Niv   %-20.20s Niv   %-20.20s\n\r",
			"Name", "Name", "Name" );
	add_buf( pBuf, buf );

	for ( i = 0; i < MAX_SKILL; ++i )
	{
		sprintf( buf, "#B%3d#b %c %-20.20s", i,
		skill_table[i].spell_fun == spell_null ? '-' : '+',
		skill_table[i].name );
		if ( i % 3 == 2 )
			strcat( buf, "\n\r" );
		else
			strcat( buf, " " );
		add_buf( pBuf, buf );
	}

	if ( i % 3 != 0 )
		add_buf( pBuf, "\n\r" );
}

void spell_list( BUFFER *pBuf )
{
	char buf[MSL];
	int i;

	sprintf( buf, "Num %-35.35s Num %-35.35s\n\r",
			"Name", "Name" );
	add_buf( pBuf, buf );

	for ( i = 0; spell_table[i].name; ++i )
	{
		sprintf( buf, "#B%3d#b %-35.35s", i,
		spell_table[i].name );
		if ( i % 2 == 1 )
			strcat( buf, "\n\r" );
		else
			strcat( buf, " " );
		add_buf( pBuf, buf );
	}

	if ( i % 2 != 0 )
		add_buf( pBuf, "\n\r" );
}

void gsn_list( BUFFER *pBuf )
{
	char buf[MSL];
	int i;

	sprintf( buf, "Num %-22.22s Num %-22.2s Num %-22.22s\n\r",
			"Name", "Name", "Name" );
	add_buf( pBuf, buf );

	for ( i = 0; gsn_table[i].name; ++i )
	{
		sprintf( buf, "#B%3d#b %-22.22s", i,
		gsn_table[i].name );
		if ( i % 3 == 2 )
			strcat( buf, "\n\r" );
		else
			strcat( buf, " " );
		add_buf( pBuf, buf );
	}

	if ( i % 3 != 0 )
		add_buf( pBuf, "\n\r" );
}

void slot_list( BUFFER *pBuf )
{
	char buf[MSL];
	int i, cnt;

	sprintf( buf, "Num %-22.22s Num %-22.2s Num %-22.22s\n\r",
			"Name", "Name", "Name" );
	add_buf( pBuf, buf );

	cnt = 0;
	for ( i = 0; i < MAX_SKILL; ++i )
	{
		if ( skill_table[i].slot )
		{
			sprintf( buf, "#B%3d#b %-22.22s",
				skill_table[i].slot,
				skill_table[i].name );
			if ( cnt % 3 == 2 )
				strcat( buf, "\n\r" );
			else
				strcat( buf, " " );
			add_buf( pBuf, buf );
			cnt++;
		}
	}

	if ( cnt % 3 != 0 )
		add_buf( pBuf, "\n\r" );
}

SKEDIT( skedit_list )
{
	BUFFER *pBuf;

	if ( IS_NULLSTR(argument) || !is_name(argument, "gsns skills spells slots") )
	{
		send_to_char( "Syntax : list [gsns/skills/spells/slots]\n\r", ch );
		return FALSE;
	}

	pBuf = new_buf();

	if ( !str_prefix( argument, "skills" ) )
		skill_list(pBuf);
	else if ( !str_prefix( argument, "spells" ) )
		spell_list(pBuf);
	else if ( !str_prefix( argument, "slots" ) )
		slot_list(pBuf);
	else if ( !str_prefix( argument, "gsns" ) )
		gsn_list(pBuf);
	else
		add_buf( pBuf, "Idiot.\n\r" );

	page_to_char( buf_string(pBuf), ch );

	free_buf(pBuf);

	return FALSE;
}

SKEDIT( skedit_show )
{
	struct skill_type *pSkill;
	char buf[MAX_STRING_LENGTH];
	char buf2[MSL];
	int i, sn = 0;

	EDIT_SKILL( ch, pSkill );

	sprintf( buf,	"Name     : [%s]\n\r",
		pSkill->name );
	send_to_char( buf, ch );

	while ( (sn < MAX_SKILL) && (pSkill != &skill_table[sn]) )
		sn++;

	if ( sn != MAX_SKILL )
	{
		sprintf( buf, "Sn       : [%3d/%3d]\n\r", sn, MAX_SKILL );
		send_to_char( buf, ch );
	}

	sprintf( buf, "Clase    + " );

	for ( i = 0; i < MAX_CLASS; ++i )
	{
		strcat( buf, class_table[i].who_name );
		strcat( buf, " " );
	}

	strcat( buf, "\n\r" );
	send_to_char( buf, ch );

	sprintf( buf, "Nivel    | " );

	for ( i = 0; i < MAX_CLASS; ++i )
	{
		sprintf( buf2, "%3d ", pSkill->skill_level[i] );
		strcat( buf, buf2 );
	}

	strcat( buf, "\n\r" );
	send_to_char( buf, ch );

	sprintf( buf, "Rating   | " );

	for ( i = 0; i < MAX_CLASS; ++i )
	{
		sprintf( buf2, "%3d ", pSkill->rating[i] );
		strcat( buf, buf2 );
	}

	strcat( buf, "\n\r" );
	send_to_char( buf, ch );

	sprintf( buf, "Spell    : [%s]\n\r", spell_name(pSkill->spell_fun) );
	send_to_char( buf, ch );

	sprintf( buf, "Target   : [%s]\n\r", flag_string(target_table,pSkill->target) );
	send_to_char( buf, ch );

	sprintf( buf, "Min Pos  : [%s]\n\r", position_table[pSkill->minimum_position].name );
	send_to_char( buf, ch );

	sprintf( buf, "pGsn     : [%s]\n\r", gsn_name(pSkill->pgsn) );
	send_to_char( buf, ch );

	sprintf( buf, "Slot     : [%d]\n\r", pSkill->slot );
	send_to_char( buf, ch );

	sprintf( buf, "Min Mana : [%d]\n\r", pSkill->min_mana );
	send_to_char( buf, ch );

	sprintf( buf, "Beats    : [%d], #B%.2f#b segundo(s).\n\r",
		pSkill->beats,
		pSkill->beats / (float) PULSE_PER_SECOND );
	send_to_char( buf, ch );

	sprintf( buf, "Noun Dmg : [%s]\n\r", pSkill->noun_damage );
	send_to_char( buf, ch );

	sprintf( buf, "Msg Off  : [%s]\n\r", pSkill->msg_off );
	send_to_char( buf, ch );

	sprintf( buf, "Msg Obj  : [%s]\n\r", pSkill->msg_obj );
	send_to_char( buf, ch );

	return FALSE;
}

void crear_tabla_hash_skills( void )
{
	int sn, valor;
	struct skhash *dato, *temp;

	for ( sn = 0; sn < MAX_SKILL; sn++ )
	{
		if ( IS_NULLSTR(skill_table[sn].name) )
			continue;

		valor = (int) (LOWER(skill_table[sn].name[0]) - 'a');

		if ( valor < 0 || valor > 25 )
		{
			bug( "Crear_tabla_hash_skills : value %d invalid", valor );
			exit(1);
		}

		dato				= new_skhash();
		dato->sn			= sn;

		/* Ahora linkear a la tabla */
		if ( skill_hash_table[valor] && (skill_table[sn].spell_fun == spell_null) ) /* skill */
		{
			/* Skills van al final! */
			for ( temp = skill_hash_table[valor]; temp; temp = temp->next )
				if ( temp->next == NULL )
					break;

			if ( !temp || temp->next )
			{
				bug( "Skill_hash_table : ???", 0 );
				exit(1);
			}

			temp->next		= dato;
			dato->next		= NULL;
		}
		else
		{
			dato->next			= skill_hash_table[valor];
			skill_hash_table[valor]		= dato;
		}
	}
}

void borrar_tabla_hash_skills( void )
{
	struct skhash *temp, *temp_next;
	int i;

	for ( i = 0; i < 26; ++i )
	{
		for ( temp = skill_hash_table[i]; temp; temp = temp_next )
		{
			temp_next = temp->next;
			free_skhash( temp );
		}
		skill_hash_table[i] = NULL;
	}
}

#if !defined(FIRST_BOOT)
SKEDIT( skedit_name )
{
	struct skill_type *pSkill;

	EDIT_SKILL( ch, pSkill );

	if ( IS_NULLSTR(argument) )
	{
		send_to_char( "Syntax : name [name]\n\r", ch );
		return FALSE;
	}

	if ( skill_lookup(argument) != -1 )
	{
		send_to_char( "A skill/spell with that name already exists.\n\r", ch );
		return FALSE;
	}

	free_string(pSkill->name);
	pSkill->name = str_dup(argument);

	borrar_tabla_hash_skills();
	crear_tabla_hash_skills();

	send_to_char( "Ok.\n\r", ch );
	return TRUE;
}

SKEDIT( skedit_slot )
{
	struct skill_type *pSkill;

	EDIT_SKILL( ch, pSkill );

	if ( IS_NULLSTR(argument) || !is_number(argument) || atoi(argument) < 0 )
	{
		send_to_char( "Syntax : slot [number]\n\r", ch );
		return FALSE;
	}

	if ( slot_lookup(atoi(argument)) != -1 )
	{
		send_to_char( "That slot is already occupied.\n\r", ch );
		return FALSE;
	}

	pSkill->slot = atoi(argument);

	send_to_char( "Ok.\n\r", ch );
	return TRUE;
}

SKEDIT( skedit_nivel )
{
	struct skill_type *pSkill;
	char arg[MIL];
	int clase, nivel;

	EDIT_SKILL(ch,pSkill);

	argument = one_argument( argument, arg );

	if ( IS_NULLSTR(argument) || IS_NULLSTR(arg) || !is_number(argument) )
	{
		send_to_char( "Syntax : level [class] [level]\n\r", ch );
		return FALSE;
	}

	if ( (clase = class_lookup(arg)) == -1 )
	{
		send_to_char( "SKEdit : Class does not exist.\n\r", ch );
		return FALSE;
	}

	nivel = atoi(argument);

	if ( nivel < 0 || nivel > MAX_LEVEL )
	{
		send_to_char( "SKEdit : Level invalid.\n\r", ch );
		return FALSE;
	}

	pSkill->skill_level[clase] = nivel;
	send_to_char( "Ok.\n\r", ch );
	return TRUE;
}

SKEDIT( skedit_rating )
{
	struct skill_type *pSkill;
	char arg[MIL];
	int rating, clase;

	EDIT_SKILL(ch,pSkill);

	argument = one_argument( argument, arg );

	if ( IS_NULLSTR(argument) || IS_NULLSTR(arg) || !is_number(argument) )
	{
		send_to_char( "Syntax : rating [class] [level]\n\r", ch );
		return FALSE;
	}

	if ( (clase = class_lookup(arg)) == -1 )
	{
		send_to_char( "SKEdit : Class does not exist.\n\r", ch );
		return FALSE;
	}

	rating = atoi(argument);

	if ( rating < 0 )
	{
		send_to_char( "SKEdit : Rating invalid.\n\r", ch );
		return FALSE;
	}

	pSkill->rating[clase] = rating;
	send_to_char( "Ok.\n\r", ch );
	return TRUE;
}

SKEDIT( skedit_spell )
{
	struct skill_type *pSkill;
	SPELL_FUN *spell;

	EDIT_SKILL( ch, pSkill );

	if ( IS_NULLSTR(argument) )
	{
		send_to_char( "Syntax : spell [name-spell]\n\r", ch );
		send_to_char( "           spell spell_null\n\r", ch );
		return FALSE;
	}

	if ( ( spell = spell_function(argument) ) == spell_null
	    && str_cmp(argument,"spell_null") )
	{
		send_to_char( "SKEdit : Spell does not exist.\n\r", ch );
		return FALSE;
	}

	pSkill->spell_fun = spell;
	send_to_char( "Ok.\n\r", ch );
	return TRUE;
}

SKEDIT( skedit_gsn )
{
	struct skill_type *pSkill;
	sh_int *gsn;
	int sn;

	EDIT_SKILL( ch, pSkill );

	if ( IS_NULLSTR(argument) )
	{
		send_to_char( "Syntax : gsn [name-gsn]\n\r", ch );
		send_to_char( "           gsn null\n\r", ch );
		return FALSE;
	}

	gsn = NULL;

	if ( str_cmp(argument,"null") && ( gsn = gsn_lookup(argument) ) == NULL )
	{
		send_to_char( "SKEdit : Gsn does not exist.\n\r", ch );
		return FALSE;
	}

	pSkill->pgsn = gsn;
	for ( sn = 0; sn < MAX_SKILL; sn++ )
	{
	    if ( skill_table[sn].pgsn != NULL )
		*skill_table[sn].pgsn = sn;
	}

	send_to_char( "Ok.\n\r", ch );
	return TRUE;
}

SKEDIT( skedit_new )
{
	DESCRIPTOR_DATA *d;
	CHAR_DATA *tch;
	struct skill_type *new_table;
	bool *tempgendata;
	sh_int *templearned;
	int i;

	if ( IS_NULLSTR(argument) )
	{
		send_to_char( "Sintaxis : new [name-of-new-skill]\n\r", ch );
		return FALSE;
	}

	if (skill_lookup(argument) != -1)
	{
		send_to_char ("A skill with that name already exists!\n\r",ch);
		return FALSE;
	}

	for ( d = descriptor_list; d; d = d->next )
	{
		if ( d->connected != CON_PLAYING || (tch = CH(d)) == NULL || tch->desc == NULL )
			continue;

		if ( tch->desc->editor == ED_SKILL )
			edit_done(tch);
	}

	/* reallocate the table */
	MAX_SKILL++;
	new_table = realloc (skill_table, sizeof(struct skill_type) * (MAX_SKILL + 1));

	if (!new_table) /* realloc failed */
	{
		send_to_char ("Failure in realloc. Brace for the impact.\n\r",ch);
		return FALSE;
	}

	skill_table					= new_table;

	skill_table[MAX_SKILL-1].name			= str_dup (argument);
	for ( i = 0; i < MAX_CLASS; ++i )
	{
		skill_table[MAX_SKILL-1].skill_level[i]	= 53;
		skill_table[MAX_SKILL-1].rating[i]	= 0;
	}
	skill_table[MAX_SKILL-1].spell_fun		= spell_null;
	skill_table[MAX_SKILL-1].target			= TAR_IGNORE;
	skill_table[MAX_SKILL-1].minimum_position	= POS_STANDING;
	skill_table[MAX_SKILL-1].pgsn			= NULL;
	skill_table[MAX_SKILL-1].slot			= 0;
	skill_table[MAX_SKILL-1].min_mana		= 0;
	skill_table[MAX_SKILL-1].beats			= 0;
	skill_table[MAX_SKILL-1].noun_damage		= str_dup( "" );
	skill_table[MAX_SKILL-1].msg_off		= str_dup( "" );
	skill_table[MAX_SKILL-1].msg_obj		= str_dup( "" );

	skill_table[MAX_SKILL].name			= NULL;

	for ( d = descriptor_list; d; d = d->next )
	{
		if ( (d->connected == CON_PLAYING)
		||  ((tch = CH(d)) == NULL)
		||  (tch->gen_data == NULL) )
			continue;

		tempgendata = realloc( tch->gen_data->skill_chosen, sizeof( bool ) * MAX_SKILL );
		tch->gen_data->skill_chosen			= tempgendata;
		tch->gen_data->skill_chosen[MAX_SKILL-1]	= 0;
	}

	for ( tch = char_list; tch; tch = tch->next )
		if ( !IS_NPC(tch) )
		{
			templearned = new_learned();

			/* copiamos los valores */
			for ( i = 0; i < MAX_SKILL - 1; ++i )
				templearned[i] = tch->pcdata->learned[i];

			free_learned(tch->pcdata->learned);
			tch->pcdata->learned = templearned;
		}

	borrar_tabla_hash_skills();
	crear_tabla_hash_skills();
	ch->desc->editor	= ED_SKILL;
	ch->desc->pEdit		= (void *) &skill_table[MAX_SKILL-1];

	send_to_char ("New Skill Created!.\n\r",ch);
	return TRUE;
}
#endif