/*
 * Copyright © 2004-2021 L2J Server
 * 
 * This file is part of L2J Server.
 * 
 * L2J Server is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * L2J Server is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.l2jserver.gameserver.model.actor;

import com.l2jserver.gameserver.ai.CtrlEvent;
import com.l2jserver.gameserver.enums.InstanceType;
import com.l2jserver.gameserver.instancemanager.InstanceManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.knownlist.PlayableKnownList;
import com.l2jserver.gameserver.model.actor.stat.PlayableStat;
import com.l2jserver.gameserver.model.actor.status.PlayableStatus;
import com.l2jserver.gameserver.model.actor.templates.L2CharTemplate;
import com.l2jserver.gameserver.model.effects.EffectFlag;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.entity.Instance;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.events.impl.character.OnCreatureKill;
import com.l2jserver.gameserver.model.events.returns.TerminateReturn;
import com.l2jserver.gameserver.model.quest.QuestState;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.serverpackets.EtcStatusUpdate;

/**
 * This class represents all Playable characters in the world.<br>
 * L2Playable:
 * <ul>
 * <li>L2PcInstance</li>
 * <li>L2Summon</li>
 * </ul>
 */
public abstract class L2Playable extends L2Character {
	private L2Character _lockedTarget = null;
	private L2PcInstance transferDmgTo = null;
	
	public L2Playable(int objectId, L2CharTemplate template) {
		super(objectId, template);
		setInstanceType(InstanceType.L2Playable);
		setIsInvul(false);
	}
	
	public L2Playable(L2CharTemplate template) {
		super(template);
		setInstanceType(InstanceType.L2Playable);
		setIsInvul(false);
	}
	
	@Override
	public PlayableKnownList getKnownList() {
		return (PlayableKnownList) super.getKnownList();
	}
	
	@Override
	public void initKnownList() {
		setKnownList(new PlayableKnownList(this));
	}
	
	@Override
	public PlayableStat getStat() {
		return (PlayableStat) super.getStat();
	}
	
	@Override
	public void initCharStat() {
		setStat(new PlayableStat(this));
	}
	
	@Override
	public PlayableStatus getStatus() {
		return (PlayableStatus) super.getStatus();
	}
	
	@Override
	public void initCharStatus() {
		setStatus(new PlayableStatus(this));
	}
	
	@Override
	public boolean doDie(L2Character killer) {
		final TerminateReturn returnBack = EventDispatcher.getInstance().notifyEvent(new OnCreatureKill(killer, this), this, TerminateReturn.class);
		if ((returnBack != null) && returnBack.terminate()) {
			return false;
		}
		
		// killing is only possible one time
		synchronized (this) {
			if (isDead()) {
				return false;
			}
			// now reset currentHp to zero
			setCurrentHp(0);
			setIsDead(true);
		}
		
		// Set target to null and cancel Attack or Cast
		setTarget(null);
		
		// Stop movement
		stopMove(null);
		
		// Stop HP/MP/CP Regeneration task
		getStatus().stopHpMpRegeneration();
		
		boolean deleteBuffs = true;
		
		if (isNoblesseBlessedAffected()) {
			stopEffects(L2EffectType.NOBLESSE_BLESSING);
			deleteBuffs = false;
		}
		if (isResurrectSpecialAffected()) {
			stopEffects(L2EffectType.RESURRECTION_SPECIAL);
			deleteBuffs = false;
		}
		if (isPlayer()) {
			L2PcInstance activeChar = getActingPlayer();
			
			if (activeChar.hasCharmOfCourage()) {
				if (activeChar.isInSiege()) {
					getActingPlayer().reviveRequest(getActingPlayer(), null, false, 0, 0);
				}
				activeChar.setCharmOfCourage(false);
				activeChar.sendPacket(new EtcStatusUpdate(activeChar));
			}
		}
		
		if (deleteBuffs) {
			stopAllEffectsExceptThoseThatLastThroughDeath();
		}
		
		// Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform
		broadcastStatusUpdate();
		
		if (getWorldRegion() != null) {
			getWorldRegion().onDeath(this);
		}
		
		// Notify Quest of L2Playable's death
		L2PcInstance actingPlayer = getActingPlayer();
		
		if (!actingPlayer.isNotifyQuestOfDeathEmpty()) {
			for (QuestState qs : actingPlayer.getNotifyQuestOfDeath()) {
				qs.getQuest().notifyDeath((killer == null ? this : killer), this, qs);
			}
		}
		// Notify instance
		if (getInstanceId() > 0) {
			final Instance instance = InstanceManager.getInstance().getInstance(getInstanceId());
			if (instance != null) {
				instance.notifyDeath(killer, this);
			}
		}
		
		if (killer != null) {
			L2PcInstance player = killer.getActingPlayer();
			
			if (player != null) {
				player.onKillUpdatePvPKarma(this);
			}
		}
		
		// Notify L2Character AI
		getAI().notifyEvent(CtrlEvent.EVT_DEAD);
		updateEffectIcons();
		return true;
	}
	
	public boolean checkIfPvP(L2Character target) {
		if (target == null) {
			return false; // Target is null
		}
		if (target == this) {
			return false; // Target is self
		}
		if (!target.isPlayable()) {
			return false; // Target is not a L2Playable
		}
		
		final L2PcInstance player = getActingPlayer();
		if (player == null) {
			return false; // Active player is null
		}
		
		if (player.getKarma() != 0) {
			return false; // Active player has karma
		}
		
		final L2PcInstance targetPlayer = target.getActingPlayer();
		if (targetPlayer == null) {
			return false; // Target player is null
		}
		
		if (targetPlayer == this) {
			return false; // Target player is self
		}
		if (targetPlayer.getKarma() != 0) {
			return false; // Target player has karma
		}
		
		if ((player.getClan() != null) && (targetPlayer.getClan() != null) && targetPlayer.getClan().isAtWarWith(player.getClanId()) && player.getClan().isAtWarWith(targetPlayer.getClanId()) && (player.getWantsPeace() == 0) && (targetPlayer.getWantsPeace() == 0) && !player.isAcademyMember()
			&& !targetPlayer.isAcademyMember()) {
			return true; // Target player and player has war
		}
		
		return targetPlayer.getPvpFlag() != 0;
	}
	
	/**
	 * Return True.
	 */
	@Override
	public boolean canBeAttacked() {
		return true;
	}
	
	// Support for Noblesse Blessing skill, where buffs are retained after resurrect
	public final boolean isNoblesseBlessedAffected() {
		return isAffected(EffectFlag.NOBLESS_BLESSING);
	}
	
	/**
	 * @return {@code true} if char can resurrect by himself, {@code false} otherwise
	 */
	public final boolean isResurrectSpecialAffected() {
		return isAffected(EffectFlag.RESURRECTION_SPECIAL);
	}
	
	/**
	 * @return {@code true} if the Silent Moving mode is active, {@code false} otherwise
	 */
	public boolean isSilentMovingAffected() {
		return isAffected(EffectFlag.SILENT_MOVE);
	}
	
	/**
	 * For Newbie Protection Blessing skill, keeps you safe from an attack by a chaotic character >= 10 levels apart from you.
	 * @return
	 */
	public final boolean isProtectionBlessingAffected() {
		return isAffected(EffectFlag.PROTECTION_BLESSING);
	}
	
	@Override
	public void updateEffectIcons(boolean partyOnly) {
		getEffectList().updateEffectIcons(partyOnly);
	}
	
	public boolean isLockedTarget() {
		return _lockedTarget != null;
	}
	
	public L2Character getLockedTarget() {
		return _lockedTarget;
	}
	
	public void setLockedTarget(L2Character cha) {
		_lockedTarget = cha;
	}
	
	public void setTransferDamageTo(L2PcInstance val) {
		transferDmgTo = val;
	}
	
	public L2PcInstance getTransferringDamageTo() {
		return transferDmgTo;
	}
	
	/**
	 * Handle level add.<br>
	 * <B><U> Overridden in </U> :</B>
	 * <li>L2PcInstance</li>
	 * <li>L2PetInstance</li>
	 * @param levelAdd
	 */
	public boolean addLevel(int levelAdd) {
		return false;
	}
	
	/**
	 * <B><U> Overridden in </U> :</B>
	 * <li>L2PcInstance</li>
	 * <li>L2PetInstance</li>
	 */
	public long getExp() {
		return 0;
	}
	
	/**
	 * <B><U> Overridden in </U> :</B>
	 * <li>L2PcInstance</li>
	 * <li>L2PetInstance</li>
	 * @return the SP amount
	 */
	public int getSp() {
		return 0;
	}
	
	/**
	 * Handle packets send on level change.<br>
	 * <B><U> Overridden in </U> :</B>
	 * <li>L2PcInstance</li>
	 * <li>L2PetInstance</li>
	 * @param levelIncreased
	 */
	public void onLevelChange(boolean levelIncreased) {
		
	}
	
	public abstract void doPickupItem(L2Object object);
	
	public abstract int getKarma();
	
	public abstract byte getPvpFlag();
	
	public abstract boolean useMagic(Skill skill, boolean forceUse, boolean dontMove);
	
	public abstract void storeMe();
	
	public abstract void storeEffect(boolean storeEffects);
	
	public abstract void restoreEffects();
	
	@Override
	public boolean isPlayable() {
		return true;
	}
}
