/* テスト */
/*
 * Debug Status: Not checked.
 * - Fixed __T on this page.
 */
/*
 * File: identify.c
 * Purpose: Object identification and knowledge routines
 *
 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
 * Copyright (c) 2009 Brian Bull
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    This software may be copied and distributed for educational, research,
 *    and not for profit purposes provided that this copyright and statement
 *    are included in all such copies.  Other copyrights may also apply.
 */
#include "angband.h"
#include "object/tvalsval.h"

/** Time last item was wielded */
s32b object_last_wield;

/**
 * Mark as object as fully known, a.k.a identified.
 *
 * \param o_ptr is the object to mark as identified
 */
void object_known(object_type *o_ptr)
{
	u32b flags[OBJ_FLAG_N];

	/* The object is not sensed, or "empty" */
	o_ptr->ident &= ~(IDENT_SENSE | IDENT_EMPTY);

	/* Mark as known */
	o_ptr->ident |= IDENT_KNOWN;

	/* Know all flags there are to be known */
	object_flags(o_ptr, flags);
	memcpy(o_ptr->known_flags, flags, sizeof(flags)); // TODO Check this works
}

/**
 * Mark an object as "aware".
 *
 * \param o_ptr is the object to become aware of
 */
void object_aware(object_type *o_ptr)
{
	int i;

	/* Fully aware of the effects */
	k_info[o_ptr->k_idx].aware = TRUE;

	p_ptr->notice |= PN_SQUELCH;
	apply_autoinscription(o_ptr);

	for (i = 1; i < o_max; i++)
	{
		const object_type *floor_o_ptr = &o_list[i];

		/* Some objects change tile on awareness */
		/* So update display for all floor objects of this kind */
		if (!floor_o_ptr->held_m_idx &&
				floor_o_ptr->k_idx == o_ptr->k_idx)
			lite_spot(floor_o_ptr->iy, floor_o_ptr->ix);
	}
}

/**
 * Set the ID flag on an object if known and actual flags are the same.
 */
static void tweak_id(object_type *o_ptr)
{
	u32b f[OBJ_FLAG_N];

	object_flags(o_ptr, f);
	o_ptr->known_flags[2] |= (f[2] & TR2_EASY_KNOW);

	if (memcmp(f, o_ptr->known_flags, _countof(f)) == 0)
	{
		object_aware(o_ptr);
		object_known(o_ptr);
	}
}


/**
 * Mark an object as "tried".
 *
 * \param o_ptr is the object to mark
 */
void object_tried(object_type *o_ptr)
{
	k_info[o_ptr->k_idx].tried = TRUE;
	o_ptr->ident |= IDENT_TRIED;
}

/**
 * Notice slays on wielded items, and additionally one kind of ammo.
 *
 * \param known_f0 is the list of flags to notice
 * \param inven_idx is the index of the inventory item to notice, or -1
 */
void object_notice_slays(u32b known_f0, int inven_idx)
{
	int i;
	UNREFERENCED_PARAMETER(inven_idx);

	/* XXX pay attention to inven_idx */

	for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
	{
		object_type *o_ptr = &inventory[i];

		o_ptr->known_flags[0] |= known_f0;
		tweak_id(o_ptr);
	}	

	return;
}

typedef struct
{
	int flagset;
	u32b flag;
	const _TCHAR *msg;
} flag_message_t;

static const flag_message_t msgs[] =
{
	{ 0, TR0_SEARCH,	__T("Your %s assists your searching.") },
	{ 1, 0xffffffff,	__T("Your %s glows.") },
	{ 2, TR2_FREE_ACT,	__T("Your %s glows.") },
	{ 2, TR2_HOLD_LIFE,	__T("Your %s glows.") },
	{ 2, TR2_DRAIN_EXP,	__T("You feel your %s drain your life.") },
	{ 2, TR2_FEATHER,	__T("Your %s slows your fall.") },
	{ 2, TR2_IMPACT,	__T("Your %s causes an earthquake!") },
	{ 2, TR2_TELEPORT,	__T("Your %s teleports you.") },
};


/**
 * Notice a given special flag on wielded items.
 *
 * \param flagset is the set the flag is in
 * \param flag is teh flag to notice
 */
void object_notice_flag(int flagset, u32b flag)
{
	int i;
	size_t j;

	for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
	{
		object_type *o_ptr = &inventory[i];
		u32b f[OBJ_FLAG_N];

		object_flags(o_ptr, f);
		if ((f[flagset] & flag) &&
				!(o_ptr->known_flags[flagset] & flag))
		{
			_TCHAR o_name[80];
			object_desc(o_name, _countof(o_name), o_ptr, FALSE,
					ODESC_BASE);

			/* Notice flags */
			o_ptr->known_flags[flagset] |= flag;
			tweak_id(o_ptr);

			for (j = 0; j < N_ELEMENTS(msgs); j++)
			{
				if (msgs[j].flagset == flagset &&
						(msgs[j].flag & flag))
					msg_format(msgs[j].msg, o_name);
			}
		}
	}	

	return;
}

/**
 * Notice curses on an object.
 *
 * \param o_ptr is the object to notice curses on
 */
bool object_notice_curses(object_type *o_ptr)
{
	u32b curses;
	u32b f[OBJ_FLAG_N];
	object_flags(o_ptr, f);

	curses = (f[2] & TR2_CURSE_MASK);

	/* Know whatever curse flags there are to know */
	o_ptr->known_flags[2] |= curses;
	tweak_id(o_ptr);

	p_ptr->notice |= PN_SQUELCH;

	return (curses ? TRUE : FALSE);
}


static const flag_message_t notice_msgs[] =
{
	{ 0, TR0_STEALTH,	__T("You feel your %s affect your stealth.") },
	{ 2, TR2_SLOW_DIGEST,	__T("You feel your %s slow your metabolism.") },
	{ 2, TR2_REGEN,		__T("You feel your %s speed up your recovery.") },
	{ 2, TR2_AGGRAVATE,	__T("You feel your %s aggravate things around you.") },
	{ 2, TR2_IMPAIR_HP,	__T("You feel your %s slow your recovery.") },
	{ 2, TR2_IMPAIR_MANA,	__T("You feel your %s slow your mana recovery.") },
};

/**
 * Notice things about an object that would be noticed in time.
 */
static void object_notice_after_time(void)
{
	int i, j;

	for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
	{
		object_type *o_ptr = &inventory[i];
		_TCHAR o_name[80];
		u32b f[OBJ_FLAG_N];

		object_desc(o_name, _countof(o_name), o_ptr, FALSE, ODESC_BASE);
		object_flags(o_ptr, f);

		for (j = 0; j < N_ELEMENTS(notice_msgs); j++)
		{
			int set = notice_msgs[j].flagset;
			u32b flag = notice_msgs[j].flag;

			if ((f[set] & flag) &&
					!(o_ptr->known_flags[set] & flag))
			{
				/* Notice the flag */
				o_ptr->known_flags[set] |= flag;
				tweak_id(o_ptr);

				/* Message */
				msg_format(notice_msgs[j].msg, o_name);
			}
		}
	}	
}

/**
 * Notice things which happen on attacking.
 */
void object_notice_on_attack(void)
{
	int i;

	for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
	{
		object_type *o_ptr = &inventory[i];

		o_ptr->ident |= IDENT_ATTACK;
		tweak_id(o_ptr);
	}

	/* XXX print message? */
	/* XXX do we need to do more about ammo? */

	return;
}

/*
 * Determine whether a weapon or missile weapon is obviously {excellent} when worn.
 */
void object_notice_on_wield(object_type *o_ptr)
{
	u32b f[OBJ_FLAG_N];
	bool obvious = FALSE;

	/* Save time of wield for later */
	object_last_wield = turn;

	/* Notice: */
	/* Damage dice and bonuses for warrior-types */


	/* Only deal with un-ID'd items */
	if (object_known_p(o_ptr)) return;

	/* Extract the flags */
	object_flags(o_ptr, f);

	/* Find obvious things */
	if (f[0] & TR0_OBVIOUS_MASK) obvious = TRUE;
	if (f[2] & TR2_OBVIOUS_MASK) obvious = TRUE;
	if (!obvious) return;

	/* Messages */
	if (wield_slot(o_ptr) == INVEN_WIELD)
	{
		if (f[0] & TR0_BRAND_POIS)
			msg_print(__T("It seethes with poison!"));
		if (f[0] & TR0_BRAND_ELEC)
			msg_print(__T("It crackles with electricity!"));
		if (f[0] & TR0_BRAND_FIRE)
			msg_print(__T("It flares with fire!"));
		if (f[0] & TR0_BRAND_COLD)
			msg_print(__T("It coats itself in ice!"));
		if (f[0] & TR0_BRAND_ACID)
			msg_print(__T("It starts spitting acid!"));
	}

	if (f[0] & TR0_STR)
		msg_format(__T("You feel %s!"), o_ptr->pval > 0 ? __T("stronger") : __T("weaker"));
	if (f[0] & TR0_INT)
		msg_format(__T("You feel %s!"), o_ptr->pval > 0 ? __T("smarter") : __T("more stupid"));
	if (f[0] & TR0_WIS)
		msg_format(__T("You feel %s!"), o_ptr->pval > 0 ? __T("wiser") : __T("more naive"));
	if (f[0] & TR0_DEX)
		msg_format(__T("You feel %s!"), o_ptr->pval > 0 ? __T("more dextrous") : __T("clumsier"));
	if (f[0] & TR0_CON)
		msg_format(__T("You feel %s!"), o_ptr->pval > 0 ? __T("healthier") : __T("sicklier"));
	if (f[0] & TR0_CHR)
		msg_format(__T("You feel %s!"), o_ptr->pval > 0 ? __T("cuter") : __T("uglier"));
	if (f[0] & TR0_SPEED)
		msg_format(__T("You feel strangely %s."), o_ptr->pval > 0 ? __T("quick") : __T("sluggish"));
	if (f[0] & (TR0_BLOWS | TR0_SHOTS))
		msg_format(__T("Your hands %s"), o_ptr->pval > 0 ? __T("tingle!") : __T("ache."));
	if (f[0] & TR0_INFRA)
		msg_format(__T("Your eyes tingle."));

	if (f[2] & TR2_LITE)
		msg_print(__T("It glows!"));
	if (f[2] & TR2_TELEPATHY)
		msg_print(__T("Your mind feels strangely sharper!"));

	/* Remember the flags */
	o_ptr->ident |= IDENT_SENSE;
	o_ptr->known_flags[0] |= (f[0] & TR0_OBVIOUS_MASK);
	o_ptr->known_flags[2] |= (f[2] & TR2_OBVIOUS_MASK);
	tweak_id(o_ptr);
}

/*
 * Given an object, return a short identifier which gives some idea of what
 * the item is.
 */
obj_pseudo_t object_pseudo(const object_type *o_ptr)
{
	object_kind *k_ptr = &k_info[o_ptr->k_idx];

	if ((o_ptr->known_flags[0] & TR0_OBVIOUS_MASK) ||
			(o_ptr->known_flags[2] & TR2_OBVIOUS_MASK))
		return INSCRIP_SPLENDID;
	else if (o_ptr->ident & IDENT_INDESTRUCT)
		return INSCRIP_SPECIAL;
	else if (!(o_ptr->ident & IDENT_SENSE) && !object_known_p(o_ptr))
		return INSCRIP_UNKNOWN;
	else if (artifact_p(o_ptr))
		return INSCRIP_SPECIAL;
	else if (ego_item_p(o_ptr))
		return INSCRIP_EXCELLENT;
	else if (o_ptr->to_a == k_ptr->to_a && o_ptr->to_h == k_ptr->to_h &&
			o_ptr->to_d == k_ptr->to_d)
		return INSCRIP_AVERAGE;
	else if (o_ptr->to_a >= k_ptr->to_a && o_ptr->to_h >= k_ptr->to_h &&
			o_ptr->to_d >= k_ptr->to_d)
		return INSCRIP_MAGICAL;
	else if (o_ptr->to_a <= k_ptr->to_a && o_ptr->to_h <= k_ptr->to_h &&
			o_ptr->to_d <= k_ptr->to_d)
		return INSCRIP_MAGICAL;

	return INSCRIP_STRANGE;
}

/*
 * Sense the inventory
 */
void sense_inventory(void)
{
	int i;
	
	_TCHAR o_name[80];
	
	unsigned int rate;
	
	/* No ID when confused in a bad state */
	if (p_ptr->timed[TMD_CONFUSED]) return;

	/* Notice some things after a while */
	if (turn >= (object_last_wield + 3000))
	{
		object_notice_after_time();
		object_last_wield = 0;
	}
	
	/* Get improvement rate */
	if (cp_ptr->flags & CF_PSEUDO_ID_IMPROV)
		rate = cp_ptr->sense_base / (p_ptr->lev * p_ptr->lev + cp_ptr->sense_div);
	else
		rate = cp_ptr->sense_base / (p_ptr->lev + cp_ptr->sense_div);
	
	if (!one_in_(rate)) return;
	
	/* Check everything */
	for (i = 0; i < INVEN_TOTAL; i++)
	{
		const _TCHAR *text = NULL;

		object_type *o_ptr = &inventory[i];
		obj_pseudo_t feel;
		bool cursed;
		
		bool okay = FALSE;
		
		/* Skip empty slots */
		if (!o_ptr->k_idx) continue;
		
		/* Valid "tval" codes */
		switch (o_ptr->tval)
		{
			case TV_SHOT:
			case TV_ARROW:
			case TV_BOLT:
			case TV_BOW:
			case TV_DIGGING:
			case TV_HAFTED:
			case TV_POLEARM:
			case TV_SWORD:
			case TV_BOOTS:
			case TV_GLOVES:
			case TV_HELM:
			case TV_CROWN:
			case TV_SHIELD:
			case TV_CLOAK:
			case TV_SOFT_ARMOR:
			case TV_HARD_ARMOR:
			case TV_DRAG_ARMOR:
			{
				okay = TRUE;
				break;
			}
		}
		
		/* Skip non-sense machines */
		if (!okay) continue;
		
		/* It is known, no information needed */
		if (object_known_p(o_ptr)) continue;
		
		
		/* It has already been sensed, do not sense it again */
		if (o_ptr->ident & IDENT_SENSE)
		{
			/* Small chance of wielded, sensed items getting complete ID */
			if (!o_ptr->name1 && (i >= INVEN_WIELD) && one_in_(1000))
				do_ident_item(i, o_ptr);
			
			continue;
		}

		/* Occasional failure on inventory items */
		if ((i < INVEN_WIELD) && one_in_(5)) continue;

		/* Sense the object */
		o_ptr->ident |= IDENT_SENSE;
		cursed = object_notice_curses(o_ptr);

		/* Get the feeling */
		feel = object_pseudo(o_ptr);

		/* Stop everything */
		disturb(0, 0);

		if (cursed)
			text = __T("cursed");
		else
			text = inscrip_text[feel];

		/* Average pseudo-ID means full ID */
		if (feel == INSCRIP_AVERAGE)
		{
			do_ident_item(i, o_ptr);
		}
		else
		{
			object_desc(o_name, _countof(o_name), o_ptr, FALSE, ODESC_BASE);

			if (i >= INVEN_WIELD)
			{
				message_format(MSG_PSEUDOID, 0, __T("You feel the %s (%c) you are %s %s %s..."),
							   o_name, index_to_label(i), describe_use(i),
							   ((o_ptr->number == 1) ? __T("is") : __T("are")),
				                           text);
			}
			else
			{
				message_format(MSG_PSEUDOID, 0, __T("You feel the %s (%c) in your pack %s %s..."),
							   o_name, index_to_label(i),
							   ((o_ptr->number == 1) ? __T("is") : __T("are")),
				                           text);
			}
		}
		
		/* Set squelch flag as appropriate */
		if (i < INVEN_WIELD)
			p_ptr->notice |= PN_SQUELCH;
		
		/* Combine / Reorder the pack (later) */
		p_ptr->notice |= (PN_COMBINE | PN_REORDER);
		
		/* Redraw stuff */
		p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
	}
}
