/*
 * Copyright (C) 2005-2008 L2J_JP / 2008-2013 L2J-SFJP
 * 
 * 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 ai.individual;
/*package com.l2jfree.gameserver.instancemanager.grandbosses;*/

import java.util.List;
import java.util.concurrent.ScheduledFuture;

import javolution.util.FastList;
import jp.sf.l2j.troja.FastIntObjectMap;
import ai.npc.AbstractNpcAI;

import com.l2jserver.Config;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.SkillTable;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.skills.AbnormalVisualEffect;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.skills.L2Skill;
import com.l2jserver.gameserver.model.zone.L2ZoneType;
import com.l2jserver.gameserver.model.zone.type.L2BossZone;
import com.l2jserver.gameserver.network.serverpackets.ExShowScreenMessage;
import com.l2jserver.gameserver.network.serverpackets.ExShowScreenMessageNPCString;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jserver.gameserver.network.serverpackets.PlaySound;
import com.l2jserver.gameserver.network.serverpackets.SocialAction;
import com.l2jserver.gameserver.network.serverpackets.SpecialCamera;
import com.l2jserver.gameserver.util.Broadcast;
import com.l2jserver.util.Rnd;

/**
 * High Priestess van Halter
 * 邪教の司祭長 アンドレアス ヴァン ホルター
 * 
 * ----/--/-- L2J_JP CREATE SANDMAN
 * 2009/07/29 JOJO Sync: l2jfree-datapack rev5271, timestamp 2009/03/05 01:50
 * 2009/08/12 JOJO Sync: l2jfree-core rev6689, timestamp 2009/07/22 23:45
 * 2009/10/29 JOJO Sync: l2jfree-core rev7164, timestamp 2009/09/20 04:37
 * 2010/05/23 JOJO Sync: l2jfree-core rev8386, timestamp 2010/05/13 23:44
 * 2010/06/04 JOJO vanhalter.py を Vanhalter.java に書き換え
 * 2010/08/26 JOJO VanhalterManager.java と Vanhalter.java を統合
 */

public class Vanhalter extends AbstractNpcAI
{
	private static final String qn = "vanhalter";

	// Config
	// プレイヤー全滅時など、司祭長が生きたままインターバルになったときのインターバル時間[㍉秒]
	private static final int INTERVAL_OF_HALTER_FIGHTING = 1200000;	// 20Min
	// トリオルの啓示の破壊→再出現時間[秒]
	private static final int TRIOL_REVELATION_RESPAWN_DELAY = 21600; // 6H
	// トリオルの啓示インターバル中は消す
	private static final boolean TRIOL_REVELATION_INTERVAL_DELETE = false;

	// NPC
	private static final int				ANDREAS_VAN_HALTER			= 29062;	//Andreas Van Halter
	private static final int				ANDREAS_CAPTAIN				= 22188;

	private static final byte DORMANT  = 0;	//司祭長は３階でじっとしている。無敵状態。
	private static final byte FIGHTING = 1;	//司祭長 戦闘状態
	@SuppressWarnings("unused") private static final byte DEAD     = 2;
	private static final byte INTERVAL = 3;	//司祭長 死亡～湧き待ち
	private String[] stateName = { "DORMANT", "FIGHTING", "DEAD", "INTERVAL" };

	// List of intruders.
	private FastIntObjectMap<List<L2PcInstance>>	_bleedingPlayers		= new FastIntObjectMap<>();
	private long _bleedTimeout;

	// Instance of monsters.
	private List<L2Npc>						_royalGuard					= new FastList<>();
	private L2MonsterInstance				_royalGuardCaptain;
	protected List<L2Npc>					_royalGuardHepler			= new FastList<>();
	private List<L2Npc>						_triolRevelation			= new FastList<>();
	private List<L2Npc>						_guardOfAltar				= new FastList<>();
	private final L2DoorInstance[]			_doorOfAltar;				//祭壇の扉(2) １階：控えの間～２階：バルコニー
	private final L2DoorInstance[]			_doorOfSacrifice;			//生贄の扉(2) ２階：バルコニー～３階：ラスボス部屋
	protected L2Npc							_ritualOffering;
	private L2Npc							_ritualSacrifice;
	protected L2GrandBossInstance			_vanHalter;
	protected L2Npc				_teleportCube0, _teleportCube1;

	// Task
	protected ScheduledFuture<?>			_movieTask					= null;
	private ScheduledFuture<?>				_doorOfAltarTask			= null;
	protected ScheduledFuture<?>			_callRoyalGuardHelperTask	= null;
	private ScheduledFuture<?>				_timeUpTask					= null;
	private ScheduledFuture<?>				_intervalTask				= null;
	protected ScheduledFuture<?>			_halterEscapeTask			= null;
	private ScheduledFuture<?>				_setBleedTask				= null;
	
	// State of High Priestess van Halter
	private boolean isVanHalterSpawned() { return _vanHalter != null; }
	protected boolean isCaptainSpawned() { return _royalGuardCaptain != null; }
	private boolean							_isLocked					= false;
	private boolean							_isHelperCalled				= false;

	// Zone
	private L2BossZone _zone;

	private Vanhalter(int id, String name, String descr)
	{
		super(id, name, descr);

		// Quest NPC starter initialization
		// High Priestess van Halter
		addAttackId(ANDREAS_VAN_HALTER);
		addKillId(ANDREAS_VAN_HALTER);
		// Andreas' Captain of the Royal Guard
		addKillId(ANDREAS_CAPTAIN);
		// Triol's Revelation
		for (int npc : new int[]{ 32058, 32059, 32060, 32061, 32062, 32063, 32064, 32065, 32066, 32067, 32068 }) {
			addKillId(npc);
			addAggroRangeEnterId(npc);
			addAttackId(npc);
		}

		if ((_zone = GrandBossManager.getInstance().getZone(-16397, -53308, -10448)) == null)
			_log.warning("AI script: Failed to load zone 'Altar of Sacrifice'");
		else
			addEnterZoneId(_zone.getId());

		// Clear flag.
		_isLocked = false;
		_isHelperCalled = false;

		// Doors.
		_doorOfAltar = new L2DoorInstance[]{
			DoorTable.getInstance().getDoor(19160014),	//祭壇の扉
			DoorTable.getInstance().getDoor(19160015),	//祭壇の扉
		};
		_doorOfSacrifice = new L2DoorInstance[]{
			DoorTable.getInstance().getDoor(19160016),	//生贄の扉
			DoorTable.getInstance().getDoor(19160017),	//生贄の扉
		};
		openDoorOfAltar(true);
		closeDoorOfSacrifice();

		// Check state of High Priestess van Halter.
		StatsSet info = GrandBossManager.getInstance().getStatsSet(ANDREAS_VAN_HALTER);
		int status = GrandBossManager.getInstance().getBossStatus(ANDREAS_VAN_HALTER);
		_log.info("VanHalter : State of High Priestess van Halter is " + stateName[status] + ".");
		if (status == INTERVAL && System.currentTimeMillis() < info.getLong("respawn_time"))
			enterInterval();
		else
			setupAltar();

		_log.info("VanHalter : initialized.");
	}

	@Override
	public String onAttack(L2Npc npc, L2PcInstance player, int damage, boolean isPet)
	{
		switch (npc.getId()) {
		case ANDREAS_VAN_HALTER:
			if (npc.getStatus().getCurrentHp() / npc.getMaxHp() <= 0.20)	// HP less than 20%
				callRoyalGuardHelper();
			break;
		case 32058: case 32059: case 32060: case 32061: case 32062: case 32063: case 32064: case 32065: case 32066:	// Triol's Revelation
		case 32067: case 32068:
			enableBleeding();
			break;
		default:
			throw new IllegalArgumentException("npcId is " + npc.getId());
		}
		return null;
	}

	@Override
	public String onKill(L2Npc npc, L2PcInstance player, boolean isPet)
	{
		if (npc instanceof L2MonsterInstance && ((L2MonsterInstance)npc).hasMinions())
			for (L2MonsterInstance m : ((L2MonsterInstance)npc).getMinionList().getSpawnedMinions())
				deleteMeifAlive(m);
		final int npcId = npc.getId();
		switch (npcId) {
		case ANDREAS_CAPTAIN:
			onKillRoyalGuardCaptain();
			break;
		case ANDREAS_VAN_HALTER:
			onKillVanhalter(npc, player);
			break;
		case 32058: case 32059: case 32060: case 32061: case 32062: case 32063: case 32064: case 32065: case 32066:	// Triol's Revelation
			checkTriolRevelationDestroy(npc);
			/*NOT break*/
		case 32067: case 32068:
			removeBleeding(npcId);
			break;
		default:
			throw new IllegalArgumentException("npcId is " + npc.getId());
		}
		return null;
	}

	@Override
	public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isPet)
	{
		// Triol's Revelation
		enableBleeding();
		return null;
	}

	//TODO:
 //	private void setUnspawn()	//プレイヤー全滅時
 //	{
 //		// プレイヤー全滅時
 //		// Check Annihilated
 //		if (_zone.isPlayersAnnihilated())
 //		{
 //			ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()
 //			{
 //				public void run()
 //				{
 //					banishForeigners();
 //					if (isCaptainSpawned() || isVanHalterSpawned())
 //						enterInterval();
 //				}
 //			}, 10000);
 //		}
 //	}

	/**
	 * 22175 Royal Guard.<br>
	 * 22175 アンドレアスの親衛隊長(透明)<br>
	 * 22176 アンドレアスの親衛隊<br>
	 * 透明な方の親衛隊長。
	 */
	private void spawnRoyalGuard()
	{
		deleteRoyalGuard();

		_royalGuard.add(spawn(22175, -14960, -53437, -10629, 0, 0,  7820, 60));
		_royalGuard.add(spawn(22175, -14964, -53766, -10603, 0, 0, 20066, 60));
		_royalGuard.add(spawn(22175, -15225, -52968, -10603, 0, 0, 55924, 60));
		_royalGuard.add(spawn(22175, -15522, -52625, -10629, 0, 0, 17737, 60));
		_royalGuard.add(spawn(22175, -15676, -52576, -10603, 0, 0, 23075, 60));
		_royalGuard.add(spawn(22175, -15879, -52521, -10603, 0, 0, 63322, 60));
		_royalGuard.add(spawn(22175, -16420, -52481, -10603, 0, 0,  4302, 60));
		_royalGuard.add(spawn(22175, -16590, -52575, -10603, 0, 0, 11742, 60));
		_royalGuard.add(spawn(22175, -16835, -52485, -10603, 0, 0, 40331, 60));
		_royalGuard.add(spawn(22175, -17051, -52639, -10629, 0, 0,  4607, 60));
		_royalGuard.add(spawn(22175, -17461, -52839, -10603, 0, 0, 13423, 60));
		_royalGuard.add(spawn(22175, -17604, -53050, -10603, 0, 0, 39469, 60));
		_royalGuard.add(spawn(22175, -17641, -53350, -10629, 0, 0, 14056, 60));
		_royalGuard.add(spawn(22175, -17710, -53768, -10603, 0, 0, 47067, 60));
		_royalGuard.add(spawn(22175, -17753, -53950, -10629, 0, 0, 14260, 60));
		_royalGuard.add(spawn(22175, -17841, -54312, -10603, 0, 0, 14180, 60));
		_royalGuard.add(spawn(22176, -16156, -47121, -10821, 0, 0, 16129, 60));
		_royalGuard.add(spawn(22176, -16157, -46340, -10821, 0, 0, 16468, 60));
		_royalGuard.add(spawn(22176, -16164, -48534, -10917, 0, 0, 16405, 60));
		_royalGuard.add(spawn(22176, -16165, -49237, -10917, 0, 0, 16091, 60));
		_royalGuard.add(spawn(22176, -16166, -47732, -10821, 0, 0, 16430, 60));
		_royalGuard.add(spawn(22176, -16177, -49925, -10917, 0, 0, 16622, 60));
		_royalGuard.add(spawn(22176, -16198, -44753, -10725, 0, 0, 16583, 60));
		_royalGuard.add(spawn(22176, -16497, -48344, -10917, 0, 0, 16215, 60));
		_royalGuard.add(spawn(22176, -16513, -49019, -10917, 0, 0, 15756, 60));
		_royalGuard.add(spawn(22176, -16529, -46310, -10821, 0, 0, 17047, 60));
		_royalGuard.add(spawn(22176, -16530, -47027, -10821, 0, 0, 16487, 60));
		_royalGuard.add(spawn(22176, -16532, -47633, -10821, 0, 0, 16242, 60));
		_royalGuard.add(spawn(22176, -16552, -49694, -10917, 0, 0, 15784, 60));
		_royalGuard.add(spawn(22176, -16594, -45094, -10725, 0, 0, 16166, 60));
	}

	/**
	 * 22175 Royal Guard.<br>
	 * 22175 アンドレアスの親衛隊長(透明)<br>
	 * 22176 アンドレアスの親衛隊<br>
	 */
	private void deleteRoyalGuard()
	{
		for (L2Npc rg : _royalGuard)
			deleteMe(rg);
		_royalGuard.clear();
	}

	/**
	 * 32058..32068 Triol's Revelation.<br>
	 * 32058～32068 トリオルの啓示(計11体)<br>
	 * このうち32058～32066(9体)が大ボス出現条件。<br>
	 * 破壊後、一定時間で再出現。大ボスとは非同期。ただし大ボス出現時にリセット（一旦消滅し再出現）する。<br>
	 * トリオルの啓示が湧いていても大ボスが居るとは限らない。
	 */
	private void spawnTriolRevelation()
	{
		deleteTriolRevelation();

		_triolRevelation.add(spawn(32058, -12674, -52673, -10932, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32059, -12728, -54317, -11108, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32060, -14936, -53112, -11559, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32061, -15658, -53668, -11579, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32062, -16404, -53263, -11559, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32063, -17146, -53238, -11559, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32064, -17887, -53137, -11559, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32065, -20082, -54314, -11106, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32066, -20081, -52674, -10921, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));

		_triolRevelation.add(spawn(32067, -16413, -56569, -10849, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
		_triolRevelation.add(spawn(32068, -16397, -54119, -10475, 0, 0, 16384, TRIOL_REVELATION_RESPAWN_DELAY));
	}

	/**
	 * 32058..32068 Triol's Revelation.<br>
	 * 32058～32068 トリオルの啓示(計11体)<br>
	 */
	private void deleteTriolRevelation()
	{
		for (L2Npc tr : _triolRevelation)
			deleteMe(tr);
		_triolRevelation.clear();
		_bleedingPlayers.clear();
	}

	/**
	 * 22188 Royal Guard Captain.<br>
	 * 22188 アンドレアスの親衛隊長<br>
	 * ├ 22189(4) アンドレアスの親衛隊<br>
	 * └ 22190(1) 祭礼のケープゴート<br>
	 * トリオルの啓示9体倒すと出現。
	 */
	private void spawnRoyalGuardCaptain()
	{
		_royalGuardCaptain = (L2MonsterInstance)spawn(ANDREAS_CAPTAIN, -16385, -52461, -10629, 0, 0, 16384, 0);
		_royalGuardCaptain.setIsRaid(true);
		_royalGuardCaptain.setOnKillDelay(100);
	}

	/**
	 * 22188 Royal Guard Captain.<br>
	 * 22188 アンドレアスの親衛隊長<br>
	 * ├ 22189(4) アンドレアスの親衛隊<br>
	 * └ 22190(1) 祭礼のケープゴート<br>
	 */
	private void deleteRoyalGuardCaptain()
	{
		deleteMeifAlive(_royalGuardCaptain);
		_royalGuardCaptain = null;
	}

	/**
	 * 22191 Royal Guard Helper.<br>
	 * 22191 アンドレアスの親衛隊長(祭壇3階)(計6体)<br>
	 * ├ 22192(1) アンドレアスの親衛隊<br>
	 * └ 22193(1) アンドレアスの親衛隊<br>
	 * 司祭長のHPが20%を切ると出現。<br>
	 * 10秒間隔(Config.HPH_CALLROYALGUARDHELPERINTERVAL)で<br>
	 * 6組(Config.HPH_CALLROYALGUARDHELPERCOUNT)出現。
	 */
	protected void spawnRoyalGuardHepler()
	{
		_royalGuardHepler.add(spawn(22191, -16397, -53199, -10448, 80/*0*/, 80/*0*/, 16384, 60));
	}

	private void deleteRoyalGuardHepler()
	{
		for (L2Npc tr : _royalGuardHepler)
			deleteMe(tr);
		_royalGuardHepler.clear();
	}

	/**
	 * 32051 Guard Of Altar<br>
	 * 32051 祭壇の門番(計4体)
	 */
	private void spawnGuardOfAltar()
	{
		deleteGuardOfAltar();

		_guardOfAltar.add(spawn(32051, -14670, -54846, -10629, 0, 0, 16384, 60));
		_guardOfAltar.add(spawn(32051, -15548, -54836, -10448, 0, 0, 16384, 60));
		_guardOfAltar.add(spawn(32051, -18123, -54846, -10629, 0, 0, 16384, 60));
		_guardOfAltar.add(spawn(32051, -17248, -54836, -10448, 0, 0, 16384, 60));
	}

	/**
	 * 32051 Guard Of Altar<br>
	 * 32051 祭壇の門番(計4体)
	 */
	private void deleteGuardOfAltar()
	{
		for (L2Npc tr : _guardOfAltar)
			deleteMe(tr);
		_guardOfAltar.clear();
	}

	/**
	 * 29062 High Priestess van Halter.<br>
	 * 29062 邪教の司祭長 アンドレアス ヴァン ホルター<br>
	 * ├ 29063(1) アンドレアスの弟子<br>
	 * └ 29064(3) アンドレアスの弟子
	 */
	private void spawnVanHalter()
	{
		_vanHalter = (L2GrandBossInstance) spawn(ANDREAS_VAN_HALTER/*29062*/, -16397, -53308, -10448, 0, 0, 16384, 0/*172800*/);
		_vanHalter.setOnKillDelay(100);
		_vanHalter.setIsImmobilized(true);
		_vanHalter.setIsInvul(true);
		Broadcast.announceToOnlinePlayers("邪教の司祭長 アンドレアス ヴァン ホルターが登場しました。");
	}

	private void deleteVanHalter()
	{
		deleteMeifAlive(_vanHalter);
		_vanHalter = null;
	}

	/**
	 * 32038 Ritual Offering.<br>
	 * 32038 犠牲の生贄<br>
	 * 司祭長にしばかれ祭礼のケープゴートに変身させられるおじさん。
	 */
	private void spawnRitualOffering()
	{
		_ritualOffering = spawn(32038, -16397, -53199, -10448, 0, 0, 16384, 0/*172800*/);
		_ritualOffering.setIsImmobilized(true);
		_ritualOffering.setIsInvul(true);
		_ritualOffering.startParalyze();
	}

	/**
	 * 32038 Ritual Offering.<br>
	 * 32038 犠牲の生贄<br>
	 */
	protected void deleteRitualOffering()
	{
		deleteMe(_ritualOffering);
		_ritualOffering = null;
	}

	/**
	 * 22195 Ritual Sacrifice.<br>
	 * 22195 祭礼のケープゴート<br>
	 * 司祭長にしばかれた犠牲の生贄の成れの果て。
	 */
	protected void spawnRitualSacrifice()
	{
		_ritualSacrifice = spawn(22195, -16397, -53199, -10448, 0, 0, 16384, 0/*172800*/);
		_ritualSacrifice.setIsImmobilized(true);
		_ritualSacrifice.setIsInvul(true);
	}

	/**
	 * 22195 Ritual Sacrifice.<br>
	 * 22195 祭礼のケープゴート<br>
	 */
	protected void deleteRitualSacrifice()
	{
		deleteMe(_ritualSacrifice);
		_ritualSacrifice = null;
	}

	@Override public String onEnterZone(L2Character character, L2ZoneType zone)
	{
		if (character instanceof L2PcInstance)
		{
			//TODO: synchronized (...)
			// Door control.
			if (!_isLocked && isCaptainSpawned())
			{
				_isLocked = true;
				scheduleDoorOfAltarTask(new Runnable(){ @Override public void run() { closeDoorOfAltar(false); }}
					, Config.HPH_TIMEOFLOCKUPDOOROFALTAR);
			}
		}
		return super.onEnterZone(character, zone);
	}

	/**
	 * 祭壇の扉 開
	 */
	protected void openDoorOfAltar(boolean loop)
	{
		for (L2DoorInstance door : _doorOfAltar)
		{
			door.openMe();
		}

		if (loop)
		{
			_isLocked = false;
			scheduleDoorOfAltarTask(new Runnable(){
				@Override
				public void run()
				{
					closeDoorOfAltar(true);
				}
			}, Config.HPH_INTERVALOFDOOROFALTER);
		}
		else
		{
			scheduleDoorOfAltarTask(null, 0);
		}
	}

	/**
	 * 祭壇の扉 閉
	 */
	protected void closeDoorOfAltar(boolean loop)
	{
		for (L2DoorInstance door : _doorOfAltar)
		{
			door.closeMe();
		}

		if (loop)
		{
			scheduleDoorOfAltarTask(new Runnable(){
				@Override
				public void run()
				{
					openDoorOfAltar(true);
				}
			}, Config.HPH_INTERVALOFDOOROFALTER);
		}
		else
		{
			scheduleDoorOfAltarTask(null, 0);
		}
	}

	/**
	 * 祭壇の扉 スケジューリング
	 */
	private void scheduleDoorOfAltarTask(Runnable run, long delay)
	{
		if (_doorOfAltarTask != null)
			_doorOfAltarTask.cancel(false);
		_doorOfAltarTask = run == null ? null : ThreadPoolManager.getInstance().scheduleGeneral(run, delay);
	}

	/**
	 * 生贄の扉 開
	 */
	private void openDoorOfSacrifice()
	{
		for (L2DoorInstance door : _doorOfSacrifice)
		{
			door.openMe();
		}
	}

	/**
	 * 生贄の扉 閉
	 */
	private void closeDoorOfSacrifice()
	{
		for (L2DoorInstance door : _doorOfSacrifice)
		{
			door.closeMe();
		}
	}

	/**
	 * onKill() 32058...32066トリオルの啓示 死亡時
	 */
	private void checkTriolRevelationDestroy(L2Npc npc)
	{
		if (!isVanHalterSpawned())
			return;

		synchronized (_triolRevelation) {
			if (isCaptainSpawned())
				return;
	
			for (L2Npc tr : _triolRevelation)
			{
				if (tr.getId() >= 32058 && tr.getId() <= 32066)
					if (!tr.isDead())
						return;
			}
	
			spawnRoyalGuardCaptain();
		}
		npc.broadcastPacket(new ExShowScreenMessage("親衛隊が出現しました。", 10000));	//[JOJO] 22188 アンドレアスの親衛隊長
	}

	/**
	 * onKill() 22188アンドレアスの親衛隊長 死亡時
	 */
	private void onKillRoyalGuardCaptain()
	{
		if (!isVanHalterSpawned())
			return;

		if (_timeUpTask != null) {
			_timeUpTask.cancel(false);
			_timeUpTask = null;
		}

	/*	deleteRoyalGuardCaptain(); */
		deleteRoyalGuard();
		banishOtherMonsters();
		spawnGuardOfAltar();
		openDoorOfSacrifice();	//生贄の扉

		_zone.broadcastPacket(new ExShowScreenMessageNPCString(10079/*"祭壇3階の扉が開かれました。"*/, 10000));	//[JOJO]
	//	CreatureSay cs = new CreatureSay(0, SystemChatChannelId.Chat_Shout, "祭壇の門番", "祭壇3階の扉が開かれました。");	//[JOJO]
	//	CreatureSay cs = new CreatureSay(0, SystemChatChannelId.Chat_Alliance, "Altar's Gatekeeper", "The door of the 3rd floor in the altar was opened.");
	//	for (L2PcInstance pc : getPlayersInside())
	//	{
	//		pc.sendPacket(cs);
	//	}

		_vanHalter.setIsImmobilized(true);
		_vanHalter.setIsInvul(true);

		_movieTask = ThreadPoolManager.getInstance().scheduleGeneral(new Movie(1), Config.HPH_APPTIMEOFHALTER);
	}

	/**
	 * Start fight against High Priestess van Halter.
	 */
	protected void combatBeginning()
	{
		if (_timeUpTask != null)
			_timeUpTask.cancel(false);
		_timeUpTask = ThreadPoolManager.getInstance().scheduleGeneral(new TimeUp(), Config.HPH_FIGHTTIMEOFHALTER);

		FastList<L2PcInstance> targets = new FastList<>();
		for (L2PcInstance pc : _vanHalter.getKnownList().getKnownPlayers().values())
			if (! pc.isDead() && Math.abs(_vanHalter.getZ() - pc.getZ()) <= 120)
				targets.add(pc);
		if (targets.size() > 0)
			_vanHalter.reduceCurrentHp(1, targets.get(Rnd.get(targets.size())), null);
	}

	/**
	 * Call Royal Guard Helper and escape from player.
	 * onAttack() 29062邪教の司祭長 アンドレアス ヴァン ホルター を攻撃時
	 */
	private void callRoyalGuardHelper()
	{
		//TODO: synchronized (...)
		if (!_isHelperCalled)
		{
			_isHelperCalled = true;
			_halterEscapeTask = ThreadPoolManager.getInstance().scheduleGeneral(new HalterEscape(), 500);
			_callRoyalGuardHelperTask = ThreadPoolManager.getInstance().scheduleGeneral(new CallRoyalGuardHelper(), 1000);
			_zone.broadcastPacket(new ExShowScreenMessage("親衛隊が出現しました。", 10000));	//[JOJO] 22191 アンドレアスの親衛隊長
		}
	}

	protected class CallRoyalGuardHelper implements Runnable
	{
		@Override
		public void run()
		{
			_callRoyalGuardHelperTask = null;
			if (!_vanHalter.isDead())
			{
				spawnRoyalGuardHepler();
				if (_royalGuardHepler.size() < Config.HPH_CALLROYALGUARDHELPERCOUNT)
				{
					_callRoyalGuardHelperTask = ThreadPoolManager.getInstance().scheduleGeneral(this, Config.HPH_CALLROYALGUARDHELPERINTERVAL);
				}
			}
		}
	}

	protected class HalterEscape implements Runnable
	{
		@Override
		public void run()
		{
			if (!_vanHalter.isDead() && _royalGuardHepler.size() <  Config.HPH_CALLROYALGUARDHELPERCOUNT)
		//	if (_royalGuardHepler.size() <= Config.HPH_CALLROYALGUARDHELPERCOUNT && !_vanHalter.isDead())
			{
				if (_vanHalter.isAfraid())
				{
					stopFear(_vanHalter);
				}
				else
				{
					startFear(_vanHalter);
					if (_vanHalter.getZ() >= -10476)
					{
						Location pos = new Location(-16397, -53308, -10448, 0);
						if (_vanHalter.getX() == pos.getX() && _vanHalter.getY() == pos.getY())
						{
							stopFear(_vanHalter);
						}
						else
						{
							_vanHalter.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, pos);
						}
					}
					else if (_vanHalter.getX() >= -16397)
					{
						Location pos = new Location(-15548, -54830, -10475, 0);
						_vanHalter.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, pos);
					}
					else
					{
						Location pos = new Location(-17248, -54830, -10475, 0);
						_vanHalter.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, pos);
					}
				}
				if (_halterEscapeTask != null)
					_halterEscapeTask.cancel(false);
				_halterEscapeTask = ThreadPoolManager.getInstance().scheduleGeneral(this, 5000);
			}
			else
			{
				stopFear(_vanHalter);
				if (_halterEscapeTask != null)
					_halterEscapeTask.cancel(false);
				_halterEscapeTask = null;
			}
		}
		
		private final void startFear(L2Character ch)
		{
			if ((ch.getAbnormalVisualEffects() & AbnormalVisualEffect.DOT_SOIL.getMask()) == 0)
				ch.startAbnormalVisualEffect(true, AbnormalVisualEffect.DOT_SOIL);
		}
		
		private final void stopFear(L2Character ch)
		{
			if ((ch.getAbnormalVisualEffects() & AbnormalVisualEffect.DOT_SOIL.getMask()) != 0)
				ch.stopAbnormalVisualEffect(true, AbnormalVisualEffect.DOT_SOIL);
		}
	}

	/**
	 * Check bleeding player.
	 */
	protected void addBleeding()
	{
		boolean existPlayers = false;
		L2Skill bleed = SkillTable.getInstance().getInfo(4615, 12);

		for (L2Npc tr : _triolRevelation)
		{
			if (tr.isDead() || ! tr.isVisible())
				continue;
			if (tr.getKnownList().getKnownPlayers().size() == 0)
				continue;
			existPlayers = true;
			_bleedTimeout = 0;
			List<L2PcInstance> seen = (List<L2PcInstance>)tr.getKnownList().getKnownPlayersInRadius(tr.getAggroRange());
			if (seen.size() == 0)
				continue;

			for (L2PcInstance pc : seen)
			{
				if (pc.getFirstEffect(bleed) == null)
				{
					bleed.applyEffects(tr, pc);
					tr.broadcastPacket(new MagicSkillUse(tr, pc, bleed.getId(), 12, bleed.getHitTime(),
							bleed.getReuseDelay()));
				}
			}
			_bleedingPlayers.put(tr.getId(), seen);
		}
		if (! existPlayers)	//[JOJO] Disable bleeding.
		{
			if (++_bleedTimeout > 120000 / 2000) {
				if (_setBleedTask != null) {
					_setBleedTask.cancel(false);
					_setBleedTask = null;
				}
				_bleedTimeout = 0;
			}
		}
	}

	/**
	 * onKill() 32058～トリオルの啓示 死亡時
	 */
	private void removeBleeding(int npcId)
	{
		List<L2PcInstance> list = _bleedingPlayers.remove(npcId);
		if (list == null)
			return;
		for (L2PcInstance pc : list)
			if (pc.getFirstEffect(L2EffectType.DMG_OVER_TIME) != null)
				pc.stopEffects(L2EffectType.DMG_OVER_TIME);
	}

	/**
	 * onAggroRangeEnter() 32058～トリオルの啓示 接近時 [JOJO]
	 */
	private synchronized void enableBleeding()
	{
		// Set bleeding to players.
		if (_setBleedTask == null) {
			_setBleedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new Bleeding(), 2000, 2000);
			_bleedTimeout = 0;
		}
	}

	protected class Bleeding implements Runnable
	{
		@Override
		public void run()
		{
			addBleeding();
		}
	}

	/**
	 * Cancel all task
	 */
	@Override
	protected void cancelAllQuestTimers()
	{
		super.cancelAllQuestTimers();
		
		if (_callRoyalGuardHelperTask != null) {
			_callRoyalGuardHelperTask.cancel(false);
			_callRoyalGuardHelperTask = null;
		}
		if (_halterEscapeTask != null) {
			_halterEscapeTask.cancel(false);
			_halterEscapeTask = null;
		}
		if (_intervalTask != null) {
			_intervalTask.cancel(false);
			_intervalTask = null;
		}
		if (_movieTask != null) {
			_movieTask.cancel(false);
			_movieTask = null;
		}
		if (_timeUpTask != null) {
			_timeUpTask.cancel(false);
			_timeUpTask = null;
		}
		scheduleDoorOfAltarTask(null, 0);
//		if (_doorOfAltarTask != null) {
//			_doorOfAltarTask.cancel(false);
//			_doorOfAltarTask = null;
//		}
	}

	/**
	 * High Priestess van Halter dead or time up. Boss status INTERVAL<br>
	 */
	protected void enterInterval()
	{
		// Cancel all task
		cancelAllQuestTimers();

		// Delete monsters
		boolean isHalterFighting = isVanHalterSpawned() && ! _vanHalter.isDead() && ! _vanHalter.isInvul();	//[JOJO]
		deleteVanHalter();
		if (TRIOL_REVELATION_INTERVAL_DELETE)
			deleteTriolRevelation();
		deleteRoyalGuardHepler();
		deleteRoyalGuardCaptain();
		deleteRoyalGuard();
		deleteRitualSacrifice();
		deleteRitualOffering();
		deleteGuardOfAltar();

		// Set interval end.
		if (GrandBossManager.getInstance().getBossStatus(ANDREAS_VAN_HALTER) != INTERVAL)
		{
			final long interval = isHalterFighting ? Math.min(Config.HPH_FIXINTERVALOFHALTER, INTERVAL_OF_HALTER_FIGHTING)
                    : Rnd.get(Config.HPH_FIXINTERVALOFHALTER - Config.HPH_RANDOMINTERVALOFHALTER, Config.HPH_FIXINTERVALOFHALTER + Config.HPH_RANDOMINTERVALOFHALTER);
			StatsSet info = GrandBossManager.getInstance().getStatsSet(ANDREAS_VAN_HALTER);
			info.set("respawn_time", System.currentTimeMillis() + interval);
			GrandBossManager.getInstance().setStatsSet(ANDREAS_VAN_HALTER, info);
			GrandBossManager.getInstance().setBossStatus(ANDREAS_VAN_HALTER, INTERVAL);
		}

		StatsSet info = GrandBossManager.getInstance().getStatsSet(ANDREAS_VAN_HALTER);
		_intervalTask = ThreadPoolManager.getInstance().scheduleGeneral(new Interval(), info.getLong("respawn_time") - System.currentTimeMillis());
		_log.info("VanHalter : Interval START.");
		_log.info("VanHalter : Next spawn date of High Priestess van Halter is " + GrandBossManager.respawnTimeFormat(info) + ".");	//[JOJO]
	}

	/**
	 * Interval.
	 */
	protected class Interval implements Runnable
	{
		@Override
		public void run()
		{
			setupAltar();
		}
	}

	/**
	 * Interval end. Boss status DORMANT
	 */
	protected void setupAltar()
	{
		// Cancel all task
		cancelAllQuestTimers();

		//[JOJO] 司祭長出現時、すでにボスゾーンに居るプレイヤーは闇の扉の外側へ強制退去
		for (L2PcInstance pc : getPlayersInside())
			pc.teleToLocation(-16397 + Rnd.get(-500, 500), -51050 + Rnd.get(-500, 500), -11017);

		// Delete all monsters
		deleteVanHalter();
		deleteTriolRevelation();
		deleteRoyalGuardHepler();
		deleteRoyalGuardCaptain();
		deleteRoyalGuard();
		deleteRitualSacrifice();
		deleteRitualOffering();
		deleteGuardOfAltar();

		// Clear flag.
		_isLocked = false;
		_isHelperCalled = false;

		// Set door state
		closeDoorOfSacrifice();	//生贄の扉
		openDoorOfAltar(true);	//祭壇の扉

		// Respawn monsters.
		spawnTriolRevelation();
		spawnRoyalGuard();
		spawnRitualOffering();
		spawnVanHalter();

		GrandBossManager.getInstance().setBossStatus(ANDREAS_VAN_HALTER, DORMANT);

		// Set time up.
		_timeUpTask = ThreadPoolManager.getInstance().scheduleGeneral(new TimeUp(), Config.HPH_ACTIVITYTIMEOFHALTER);
		_log.info("VanHalter : Spawn Van Halter.");
	}

	/**
	 * Time up.
	 */
	protected class TimeUp implements Runnable
	{
		@Override
		public void run()
		{
			enterInterval();
		}
	}

	/**
	 * Appearance movie.
	 */
	private class Movie implements Runnable
	{
		private int					_taskId;
		private List<L2PcInstance>	_players;

		public Movie(int taskId)
		{
			_taskId = taskId;
		}

		private void next(int taskId, long delay)	//[JOJO]
		{
			_taskId = taskId;
			_movieTask = ThreadPoolManager.getInstance().scheduleGeneral(this, delay);
		}

		private void specialCamera(L2Object npc, int dist, int yaw, int pitch, int time, int duration, int turn, int rise, int widescreen, int unk)
		{
			SpecialCamera packet = new SpecialCamera(npc.getObjectId(), dist, yaw, pitch, time, duration, turn, rise, widescreen, unk);
			for (L2PcInstance pc : _players)
				pc.sendPacket(packet);
		}

		// [L2J_JP ADD SANDMAN][JOJO L2PcInstanceより引越し]
		private void enterMovieMode(L2PcInstance pc)
		{
			pc.setTarget(null);
			pc.stopMove(null);			//移動中なら停止
			pc.setIsParalyzed(true);	//石化
			pc.setIsInvul(true);		//無敵
			pc.setIsImmobilized(true);	//移動禁止
		//	pc.sendPacket(new CameraMode(1));		/*仮*/
		}

		// [L2J_JP ADD SANDMAN][JOJO L2PcInstanceより引越し]
		public void leaveMovieMode(L2PcInstance pc)
		{
			pc.setTarget(null);
			pc.stopMove(null);
			pc.setIsParalyzed(false);
			pc.setIsInvul(false);
			pc.setIsImmobilized(false);
		//	pc.sendPacket(new CameraMode(0));		/*仮*/
		}

		@Override
		public void run()
		{
			_movieTask = null;

			switch (_taskId)
			{
			case 1:
				GrandBossManager.getInstance().setBossStatus(ANDREAS_VAN_HALTER, FIGHTING);
				// Lock players
				_players = getPlayersInside();
				for (L2PcInstance pc : _players)
					this.enterMovieMode(pc);
				// Lock Boss
				_vanHalter.abortAttack();
				_vanHalter.abortCast();
				_vanHalter.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
				_vanHalter.setHeading(16384);
				_vanHalter.setTarget(_ritualOffering);
				// Camera
				specialCamera(_vanHalter, 1650, 90, 89, 0, 15000, 0, 89, 0, 0);
				next(2, 50);
				break;

			case 2:
				specialCamera(_vanHalter, 1650, 90, 89, 0, 15000, 0, 89, 0, 0);
				next(3, 4000);
				break;

			case 3:
				specialCamera(_vanHalter, 1450, 90, 80, 4000, 15000, 0, 80, 0, 0);
				next(4, 1000);
				break;

			case 4:
				specialCamera(_vanHalter, 1250, 90, 70, 4000, 15000, 0, 70, 0, 0);
				next(5, 1000);
				break;

			case 5:
				specialCamera(_vanHalter, 1050, 90, 60, 4000, 15000, 0, 60, 0, 0);
				next(6, 1000);
				break;

			case 6:
				specialCamera(_vanHalter, 850, 90, 50, 4000, 15000, 0, 45, 0, 0);
				next(7, 1000);
				break;

			case 7:
				specialCamera(_vanHalter, 650, 90, 40, 4000, 15000, 0, 30, 0, 0);
				next(8, 1000);
				break;

			case 8:
				specialCamera(_vanHalter, 450, 90, 30, 4000, 15000, 0, 15, 0, 0);
				next(9, 1000);
				break;

			case 9:
				specialCamera(_vanHalter, 250, 90, 20, 4000, 15000, 0, 1, 0, 0);
				next(12, 1000);
				break;

			case 12:
				specialCamera(_vanHalter, 50, 90, 10, 4000, 15000, 0, 0, 0, 0);
				next(13, 2000);
				break;

			case 13:
				// High Priestess van Halter uses the skill to kill Ritual Offering.
				L2Skill skill = SkillTable.getInstance().getInfo(1168, 7);
				_ritualOffering.setIsInvul(false);
				_vanHalter.setTarget(_ritualOffering);
				_vanHalter.setIsImmobilized(false);
				_vanHalter.doCast(skill);
				_vanHalter.setIsImmobilized(true);

				next(14, 4700); //skill.getHitTime() + alpha.
				break;

			case 14:
				_ritualOffering.broadcastPacket(new SocialAction(_ritualOffering, 1));
				next(15, 2500);
				break;

			case 15:
				spawnRitualSacrifice();
				deleteRitualOffering();

				specialCamera(_vanHalter, 100, 90, 15, 1500, 15000, 0, 0, 0, 0);
				next(16, 3000);
				break;

			case 16:
				specialCamera(_vanHalter, 5200, 90, 10, 9500, 6000, 0, 20, 0, 0);
				next(17, 7000);
				break;

			case 17:
				// Unlock players
				for (L2PcInstance pc : _players)
					this.leaveMovieMode(pc);
				_players = null;
				// Unlock Boss
				_vanHalter.setIsImmobilized(false);
				_vanHalter.setIsInvul(false);
				next(18, 1000);
				break;

			case 18:
				deleteRitualSacrifice();
				combatBeginning();
				break;

			default:
				throw new RuntimeException();
			}
		}
	}

	protected List<L2PcInstance> getPlayersInside()
	{
		List<L2PcInstance> lst = new FastList<>();
		for (L2Character cha : _zone.getCharactersInside())
		{
			if (cha instanceof L2PcInstance)
				lst.add((L2PcInstance)cha);
		}
		return lst;
	}

	protected void banishForeigners()
	{
		_zone.oustAllPlayers();
	}

	/**
	 * その他、ボスゾーンの外から引いてきたモンスターを排除する。ムービーの邪魔になるので。
	 */
	private void banishOtherMonsters()
	{
		class Run implements Runnable
		{
			final L2Npc npc;
			Run(L2Npc mob) { npc = mob; }
			@Override
			public void run() { npc.doDie(null); }
		}
		
		for (L2Character character : _zone.getCharactersInside())
		{
			if (character instanceof L2MonsterInstance)
			{
				L2MonsterInstance mob = (L2MonsterInstance) character;
				if (!mob.isDead())
				{
					L2Spawn spawn = mob.getSpawn();
					if (spawn != null && !_zone.isInsideZone(spawn.getX(), spawn.getY(), spawn.getZ()))
						ThreadPoolManager.getInstance().scheduleGeneral(new Run(mob), Rnd.get(1000, 5000));
				}
			}
		}
	}

	/**
	 * High Priestess van Halter dead.
	 * onKill() 29062邪教の司祭長 アンドレアス ヴァン ホルター 死亡時
	 */
	private void onKillVanhalter(L2Npc npc, L2PcInstance pc)
	{
		enterInterval();

		// Teleportation Cubic -> Closest Town
		_teleportCube0 = addSpawn(1032467, -16397, -53308, -10448, 16384, false, 0);
		_teleportCube1 = addSpawn(1032467, -16397, -52040, -10607, 16384, false, 0);

		ThreadPoolManager.getInstance().scheduleGeneral(new Runnable(){
			@Override
			public void run() {
				banishForeigners();
				deleteMe(_teleportCube0);
				_teleportCube0 = null;
				deleteMe(_teleportCube1);
				_teleportCube1 = null;
			}
		}, 900000/*15minute*/);

		StatsSet info = GrandBossManager.getInstance().getStatsSet(ANDREAS_VAN_HALTER);
		Broadcast.announceToOnlinePlayers("邪教の司祭長 アンドレアス ヴァン ホルターが死亡しました。次の復活は " + GrandBossManager.respawnTimeFormat(info) + " ごろです。");
		_zone.broadcastPacket(new PlaySound(1, "BS01_D", 1, npc.getObjectId(), npc.getX(), npc.getY(), npc.getZ()));
	}

	/**
	 * 旧 vanhalter_spawnlist.sql の代替。
	 * auto respawn アリ
	 * auto despawn ナシ
	 */
	private L2Npc spawn(int npc_templateid, int locx, int locy, int locz, int randomx, int randomy, int heading, int respawn_delay)
	{
		try
		{
			L2NpcTemplate template = NpcTable.getInstance().getTemplate(npc_templateid);
			L2Spawn spawn = new L2Spawn(template);
			spawn.setXYZ(locx + Rnd.get(randomx), locy + Rnd.get(randomy), locz);
			spawn.setHeading(heading);
			if (respawn_delay > 0) {
				spawn.setRespawnDelay(respawn_delay);
				spawn.startRespawn();
			} else {
			/*	spawn.stopRespawn(); */	//default
			}
			return spawn.doSpawn();
		}
		catch (Exception e)
		{
			_log.warning("Could not spawn Npc " + npc_templateid);
			e.printStackTrace();
		}
		
		return null;
	}

	/**
	 * Safe delete npc
	 */
	protected void deleteMe(L2Npc npc)
	{
		if (npc != null)
		{
			npc.getSpawn().stopRespawn();
			npc.deleteMe();
		}
	}
	/**
	 * まだ生きているとき ... 即座に消す。<br>
	 * すでに死んでいるとき ... DecayTaskManagerで自然消滅する。
	 */
	private void deleteMeifAlive(L2Npc npc)
	{
		if (npc != null)
		{
			L2Spawn spawn;
			if ((spawn = npc.getSpawn()) != null)
				spawn.stopRespawn();
			if (! npc.isDead())
				npc.deleteMe();
		}
	}

	/**
	 * Quest class and state definition
	 */
	public static void main(String[] args)
	{
		new Vanhalter(-1, qn, "ai");
	}
}
