/*
 * Copyright (c) 2013 L2jMobius
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.l2jmobius.gameserver.managers;

import org.l2jmobius.gameserver.entity.Location;
import org.l2jmobius.gameserver.entity.WorldObject;
import org.l2jmobius.gameserver.entity.actor.Attackable;
import org.l2jmobius.gameserver.entity.actor.Creature;
import org.l2jmobius.gameserver.entity.actor.Npc;
import org.l2jmobius.gameserver.entity.actor.Player;
import org.l2jmobius.gameserver.entity.actor.instance.Monster;
import org.l2jmobius.gameserver.entity.clan.Clan;

/**
 * One view over the two kinds of bots (NPC fake players and headless
 * players) for the chat understanding layer: identity, situation and the few
 * world actions a conversation can trigger (attack the insulter, run to help,
 * team up). Everything the seam engine needs comes from here, so both kinds of
 * bots talk with the same brain.
 * @author Mobius SPP
 */
public class FakePlayerBotRef
{
	public final int botId;
	public final String personality;
	public final Creature creature;
	public final Npc npc;
	public final Player headless;

	private FakePlayerBotRef(int botId, String personality, Npc npc, Player headless)
	{
		this.botId = botId;
		this.personality = (personality == null) || personality.isEmpty() ? "neutral" : personality;
		this.npc = npc;
		this.headless = headless;
		this.creature = (npc != null) ? npc : headless;
	}

	public static FakePlayerBotRef ofNpc(Npc npc)
	{
		return new FakePlayerBotRef(FakePlayerProgressionManager.getInstance().getCharacterId(npc.getObjectId()), npc.getTemplate().getParameters().getString("fpcPersonality", "neutral"), npc, null);
	}

	public static FakePlayerBotRef ofHeadless(Player bot)
	{
		return new FakePlayerBotRef(FakePlayerHeadlessManager.getInstance().seedIdOf(bot), FakePlayerHeadlessManager.getInstance().personalityOf(bot), null, bot);
	}

	/**
	 * @param creature any creature.
	 * @return a bot reference when the creature is a fake player of either kind, otherwise null.
	 */
	public static FakePlayerBotRef of(Creature creature)
	{
		if (creature instanceof Npc npc)
		{
			return npc.isFakePlayer() ? ofNpc(npc) : null;
		}
		if ((creature instanceof Player player) && player.isHeadlessBot())
		{
			return ofHeadless(player);
		}
		return null;
	}

	public boolean isHeadless()
	{
		return headless != null;
	}

	public String getName()
	{
		return creature.getName();
	}

	public int getLevel()
	{
		if (headless != null)
		{
			return headless.getLevel();
		}
		final int level = FakePlayerProgressionManager.getInstance().getLevel(npc.getObjectId());
		return (level > 0) ? level : npc.getTemplate().getLevel();
	}

	public String getZone()
	{
		if (headless != null)
		{
			return FakePlayerHeadlessManager.getInstance().zoneOf(headless);
		}
		return FakePlayerProgressionManager.getInstance().getZoneName(npc.getObjectId());
	}

	public int getClanId()
	{
		return (headless != null) ? headless.getClanId() : npc.getFakePlayerClanId();
	}

	public Clan getClan()
	{
		if (headless != null)
		{
			return headless.getClan();
		}
		final int clanId = npc.getFakePlayerClanId();
		return (clanId > 0) ? org.l2jmobius.gameserver.data.sql.ClanTable.getInstance().getClan(clanId) : null;
	}

	public boolean isInCombat()
	{
		return creature.isInCombat();
	}

	public boolean isDead()
	{
		return creature.isDead();
	}

	public double distanceTo(WorldObject object)
	{
		return creature.calculateDistance2D(object);
	}

	/**
	 * Attacks a player (insult, clan war, gank) with the mechanics of the bot kind.
	 * @param target the player.
	 * @param seconds how long a headless bot stays focused on the target.
	 */
	public void attack(Player target, int seconds)
	{
		if (headless != null)
		{
			FakePlayerHeadlessManager.getInstance().forceTarget(headless, target, seconds);
			return;
		}
		if (npc instanceof Attackable attackable)
		{
			attackable.addDamageHate(target, 0, 500);
			npc.setRunning();
			npc.getAI().setIntentionAttack(target);
		}
	}

	/**
	 * Helps against a monster (ask_help while the player fights).
	 * @param monster the monster.
	 */
	public void assist(Monster monster)
	{
		if (headless != null)
		{
			headless.setTarget(monster);
			headless.getAI().setIntentionAttack(monster);
			return;
		}
		if (npc instanceof Attackable attackable)
		{
			attackable.addDamageHate(monster, 0, 200);
			npc.setRunning();
			npc.getAI().setIntentionAttack(monster);
		}
	}

	/**
	 * Runs to a player (ask_help with nothing to fight yet).
	 * @param player the player.
	 */
	public void moveTo(Player player)
	{
		if (headless != null)
		{
			headless.setRunning();
			headless.getAI().setIntentionFollow(player);
			return;
		}
		npc.setRunning();
		npc.getAI().setIntentionMoveTo(new Location(player.getX(), player.getY(), player.getZ()));
	}

	/**
	 * Teams up with the player: a real party invite for headless bots, escort mode for NPC bots.
	 * @param player the player.
	 */
	public void teamUp(Player player)
	{
		if (headless != null)
		{
			FakePlayerSocial.getInstance().inviteToParty(headless, player);
			return;
		}
		FakePlayerAiTaskManager.getInstance().startCompanion(npc, player, 15);
	}

	/**
	 * @param player the player.
	 * @return true when this bot invited the player recently ("yes" means accept).
	 */
	public boolean hasPendingInvite(Player player)
	{
		if (headless != null)
		{
			return FakePlayerSocial.getInstance().hasPendingInvite(headless, player);
		}
		return FakePlayerAiTaskManager.getInstance().hasPendingInvite(npc, player);
	}

	/**
	 * Says a line in local chat.
	 * @param line the text.
	 * @param seam the seam it came from (heard classification).
	 */
	public void say(String line, String seam)
	{
		if ((line == null) || line.isEmpty())
		{
			return;
		}
		if (headless != null)
		{
			FakePlayerHeadlessManager.getInstance().say(headless, line, seam);
			return;
		}
		FakePlayerChatManager.getInstance().broadcastToNearby(npc, line, seam);
	}

	/**
	 * Whispers a player.
	 * @param to the player.
	 * @param line the text.
	 */
	public void whisper(Player to, String line)
	{
		if ((line == null) || line.isEmpty())
		{
			return;
		}
		if (headless != null)
		{
			FakePlayerHeadlessManager.getInstance().whisper(headless, to, line);
			return;
		}
		FakePlayerChatManager.getInstance().sendChat(to, npc.getName(), line);
	}
}
