/*
 * Copyright (C) 2004-2013 L2J DataPack
 * 
 * This file is part of L2J DataPack.
 * 
 * L2J DataPack 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 DataPack 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;

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

import javolution.util.FastList;
import ai.npc.AbstractNpcAI;

import com.l2jserver.Config;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.datatables.SkillTable;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.model.L2Party;
import com.l2jserver.gameserver.model.L2World;
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.L2GrandBossInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.skills.L2Skill;
import com.l2jserver.gameserver.model.zone.type.L2BossZone;
import com.l2jserver.gameserver.network.NpcStringId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
import com.l2jserver.gameserver.network.serverpackets.Earthquake;
import com.l2jserver.gameserver.network.serverpackets.ExShowScreenMessage;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
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.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.util.Broadcast;
import com.l2jserver.util.Rnd;

/**
 * Antharas' AI.
 * @author L2J_JP SANDMAN
 */
public final class Antharas extends AbstractNpcAI
{
	// config
	@SuppressWarnings("unused") private static final int FWA_ACTIVITYTIMEOFANTHARAS = 120;
	// private static final int FWA_APPTIMEOFANTHARAS = 1800000;
	private static final int FWA_INACTIVITYTIME = 900000;
	private static final int FWA_CLEAR_DELAY = 1800000;
	private static final boolean FWA_OLDANTHARAS = false;
	private static final boolean FWA_MOVEATRANDOM = true;
	private static final boolean FWA_DOSERVEREARTHQUAKE = true;
	private static final int FWA_LIMITOFWEAK = 45;
	private static final int FWA_LIMITOFNORMAL = 63;
	private static final int FWA_MAXMOBS = 10; // this includes Antharas itself
	private static final int FWA_INTERVALOFMOBSWEAK = 180000;
	private static final int FWA_INTERVALOFMOBSNORMAL = 150000;
	private static final int FWA_INTERVALOFMOBSSTRONG = 120000;
	private static final int FWA_PERCENTOFBEHEMOTH = 60;
	private static final int FWA_SELFDESTRUCTTIME = 15000;
	// Location of teleport cube.
	private static final int TELEPORT_CUBE_ID = 31859;
	private final Location TELEPORT_CUBE_LOCATIONS[] =
	{
		new Location(177615, 114941, -7709, 0)
	};
	
 //	protected List<L2Spawn> _teleportCubeSpawn = new FastList<>();
	protected FastList<L2Npc> _teleportCube = new FastList<>();
 //	// Spawn data of monsters.
 //	protected Map<Integer, L2Spawn> _monsterSpawn = new FastMap<>();
	// Instance of monsters.
	protected FastList<L2Npc> _monsters = new FastList<>();
	protected L2GrandBossInstance _antharas = null;
	// Antharas Ids
	private static final int ANTHARAS_OLD_ID = 29019;
	private static final int ANTHARAS_WEAK_ID = 29066;
	private static final int ANTHARAS_NORMAL_ID = 29067;
	private static final int ANTHARAS_STRONG_ID = 29068;
//	private static final int BEHEMOTH_DRAGON = 29069;	//[JOJO]
//	private static final int DRAGON_BOMBER = 29076;		//[JOJO]

	// items
	private static final int ANTHARAS_CIRCLET = 8568;	//[JOJO]
	// Tasks.
	protected ScheduledFuture<?> _monsterSpawnTask = null;
	protected ScheduledFuture<?> _activityCheckTask = null;
	protected ScheduledFuture<?> _socialTask = null;
	protected ScheduledFuture<?> _mobiliseTask = null;
	protected ScheduledFuture<?> _mobsSpawnTask = null;
	protected ScheduledFuture<?> _selfDestructionTask = null;
	protected ScheduledFuture<?> _moveAtRandomTask = null;
	protected ScheduledFuture<?> _movieTask = null;
	// Antharas Status Tracking :
	private static final byte DORMANT = 0; // Antharas is spawned and no one has entered yet. Entry is unlocked
	private static final byte WAITING = 1; // Antharas is spawend and someone has entered, triggering a 30 minute window for additional people to enter
	// before he unleashes his attack. Entry is unlocked
	private static final byte FIGHTING = 2; // Antharas is engaged in battle, annihilating his foes. Entry is locked
	private static final byte DEAD = 3; // Antharas has been killed. Entry is locked
	
	protected static long _LastAction = 0;
	
	protected static L2BossZone _Zone;
	
	// Boss: Antharas
	private Antharas()
	{
		super(Antharas.class.getSimpleName(), "ai/individual");
		
		if (Config.ANTHARAS_SPAWN_INTERVAL - Config.ANTHARAS_SPAWN_RANDOM < (FWA_CLEAR_DELAY + 3600000 - 1) / 3600000)
		{
			Config.ANTHARAS_SPAWN_INTERVAL = Config.ANTHARAS_SPAWN_RANDOM + (FWA_CLEAR_DELAY + 3600000 - 1) / 3600000;
		//	_log.log(Level.WARNING, "ANTHARAS_SPAWN_INTERVAL = " + Config.ANTHARAS_SPAWN_INTERVAL);
		}
		
		registerMobs(ANTHARAS_OLD_ID, ANTHARAS_WEAK_ID, ANTHARAS_NORMAL_ID, ANTHARAS_STRONG_ID, 29069, 29070, 29071, 29072, 29073, 29074, 29075, 29076);
		init();
	}
	
	// Initialize
	private void init()
	{
		_Zone = GrandBossManager.getInstance().getZone(179700, 113800, -7709);
		
		int antharasId = ANTHARAS_OLD_ID;
		int status = GrandBossManager.getInstance().getBossStatus(ANTHARAS_OLD_ID);
		
		int statusWeak = GrandBossManager.getInstance().getBossStatus(ANTHARAS_WEAK_ID);
		int statusNormal = GrandBossManager.getInstance().getBossStatus(ANTHARAS_NORMAL_ID);
		int statusStrong = GrandBossManager.getInstance().getBossStatus(ANTHARAS_STRONG_ID);
		if (statusWeak == FIGHTING || statusWeak == DEAD)
		{
			antharasId = ANTHARAS_WEAK_ID;
			status = statusWeak;
		}
		else if (statusNormal == FIGHTING || statusNormal == DEAD)
		{
			antharasId = ANTHARAS_NORMAL_ID;
			status = statusNormal;
		}
		else if (statusStrong == FIGHTING || statusStrong == DEAD)
		{
			antharasId = ANTHARAS_STRONG_ID;
			status = statusStrong;
		}
		
		if (status == WAITING)
		{
			setAntharasSpawnTask();
		}
		else if (status == FIGHTING)
		{
			StatsSet info = GrandBossManager.getInstance().getStatsSet(antharasId);
			int loc_x = info.getInt("loc_x");
			int loc_y = info.getInt("loc_y");
			int loc_z = info.getInt("loc_z") - com.l2jserver.Config.NPC_SPAWN_Z_MARGIN;
			int heading = info.getInt("heading");
			int hp = info.getInt("currentHP");
			int mp = info.getInt("currentMP");
			_antharas = (L2GrandBossInstance) addSpawn(antharasId, loc_x, loc_y, loc_z, heading, false, 0);
			GrandBossManager.getInstance().addBoss(_antharas);
			_antharas.setCurrentHpMp(hp, mp);
			_LastAction = System.currentTimeMillis();
			// Start repeating timer to check for inactivity
			_activityCheckTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckActivity(), 60000, 60000);
		}
		else if (status == DEAD)
		{
			StatsSet info = GrandBossManager.getInstance().getStatsSet(antharasId);
			long respawnTime = info.getLong("respawn_time");
			if (respawnTime <= System.currentTimeMillis())
			{
				// the time has already expired while the server was offline. Immediately spawn antharas in his cave.
				// also, the status needs to be changed to DORMANT
				GrandBossManager.getInstance().setBossStatus(antharasId, DORMANT);
				status = DORMANT;
			}
			else
			{
				ThreadPoolManager.getInstance().scheduleGeneral(new UnlockAntharas(antharasId), respawnTime - System.currentTimeMillis());
				_log.info("AI script: Next spawn date of Anthras is " + GrandBossManager.respawnTimeFormat(info) + ".");
			}
		}
	}
	
	// Do spawn teleport cube.
	public void spawnCube()
	{
		for (Location pos : TELEPORT_CUBE_LOCATIONS)
		{
			_teleportCube.add(addSpawn(TELEPORT_CUBE_ID, pos, false, 0));
		}
	}
	
	// Setting Antharas spawn task.
	public void setAntharasSpawnTask()
	{
		if (_monsterSpawnTask == null)
		{
			synchronized (this)
			{
				if (_monsterSpawnTask == null)
				{
					GrandBossManager.getInstance().setBossStatus(ANTHARAS_OLD_ID, WAITING);
					Config.ANTHARAS_WAIT_TIME = Math.max(3, Config.ANTHARAS_WAIT_TIME); // [JOJO]
					_monsterSpawnTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(-3), ((Config.ANTHARAS_WAIT_TIME - 3) * 60000)); // [JOJO]
				//	_monsterSpawnTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(1), Config.ANTHARAS_WAIT_TIME);
					Broadcast.announceToOnlinePlayers("地竜アンタラスが" + Config.ANTHARAS_WAIT_TIME + "分後に目覚めます。"); // [JOJO]
				}
			}
		}
	}
	
	@Override
	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
	{
		if (event.equalsIgnoreCase("waiting"))	// <<== scripts/teleports/GrandBossTeleporters/GrandBossTeleporters.java
		{
			setAntharasSpawnTask();
		}
		
		return super.onAdvEvent(event, npc, player);
	}
	
	protected void startMinionSpawns(int antharasId)
	{
		int intervalOfMobs;
		
		// Interval of minions is decided by the type of Antharas
		// that invaded the lair.
		switch (antharasId)
		{
			case ANTHARAS_WEAK_ID:
				intervalOfMobs = FWA_INTERVALOFMOBSWEAK;
				break;
			case ANTHARAS_NORMAL_ID:
				intervalOfMobs = FWA_INTERVALOFMOBSNORMAL;
				break;
			default:
				intervalOfMobs = FWA_INTERVALOFMOBSSTRONG;
				break;
		}
		
		// Spawn mobs.
		_mobsSpawnTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new MobsSpawn(), intervalOfMobs, intervalOfMobs);
	}
	
	// Do spawn Antharas.
	private class AntharasSpawn implements Runnable
	{
		private int _taskId;
		private final Collection<L2Character> _players = _Zone.getCharactersInside();
		
		public AntharasSpawn(int taskId)
		{
			_taskId = taskId;
		}
		
		@Override
		public void run()
		{
			switch (_taskId)
			{
			//[JOJO]-------------------------------------------------
			case -3:
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(-2), 60000);
				screenMessage("3分前");
				break;
			case -2:
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(-1), 60000);
				screenMessage("2分前");
				break;
			case -1:
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(0), 60000);
				screenMessage("1分前");
				_Zone.broadcastPacket(new Earthquake(179700, 113800, -7709, 40, 15));
				break;
			//-------------------------------------------------------
			case 0: // Spawn.
				// Strength of Antharas is decided by the number of players that
				// invaded the lair.
				_monsterSpawnTask.cancel(false);
				_monsterSpawnTask = null;
				final int antharasId;
				if (FWA_OLDANTHARAS)
				{
					antharasId = 29019; // old
				}
				else if (_players.size() <= FWA_LIMITOFWEAK)
				{
					antharasId = 29066; // weak
				}
				else if (_players.size() > FWA_LIMITOFNORMAL)
				{
					antharasId = 29068; // strong
				}
				else
				{
					antharasId = 29067; // normal
				}
				
				// Do spawn.
				_antharas = (L2GrandBossInstance) addSpawn(antharasId, 185452, 114835, -8221, 0, false, 0);
				GrandBossManager.getInstance().addBoss(_antharas);
			//	_monsters.add(_antharas);
			//	_antharas.setIsImmobilized(true);
				
				GrandBossManager.getInstance().setBossStatus(ANTHARAS_OLD_ID, DORMANT);
				GrandBossManager.getInstance().setBossStatus(antharasId, FIGHTING);
				_LastAction = System.currentTimeMillis();
				// Start repeating timer to check for inactivity
				_activityCheckTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new CheckActivity(), 60000, 60000);
				_Zone.broadcastPacket(new PlaySound(1, "BS02_A", 1, _antharas.getObjectId(), 185452, 114835, -8221));	//[JOJO]
				screenMessage("覚醒");	//[JOJO]
				
				// Setting 1st time of minions spawn task.
				if (!FWA_OLDANTHARAS)
				{
					startMinionSpawns(antharasId);
				}

				// Set next task.
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
			//	_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(1), 16);
				//[JOJO]-------------------------------------------------
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new AntharasSpawn(1), 2000, 2000);
				_antharas.setIsInSocialAction(true);
				_antharas.setIsImmobilized(false);
				_antharas.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(181911, 114835, -7678, 0));
				//-------------------------------------------------------
				break;
				
			//[JOJO]-------------------------------------------------
			case 1: // "antharas_has_arrived"
				{
					int dx = Math.abs(_antharas.getX() - 181911);
					int dy = Math.abs(_antharas.getY() - 114835);
					if (dx <= 50 && dy <= 50)
					{
						// Set next task.
						if (_socialTask != null)
							_socialTask.cancel(true);
						_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(2), 16);
						_antharas.setXYZ(181911, 114835, -7678);
						_antharas.setIsImmobilized(true);
						_antharas.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
					}
					else
					{
						// Retry - scheduleGeneralAtFixedRate(new AntharasSpawn(1), 2000, 2000)
						_antharas.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(181911, 114835, -7678, 0));
					}
				}
				break;
			//-------------------------------------------------------
				
			case 2:
				// Set camera.
				broadcastPacket(new SpecialCamera(_antharas.getObjectId(), 700, 13, -19, 0, 20000, 0, 0, 1, 0));
				
				// Set next task.
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(3), 3000);
				break;
				
			case 3:
				// Do social.
				broadcastPacket(new SpecialCamera(_antharas.getObjectId(), 700, 13, 0, 6000, 20000, 0, 0, 1, 0));
				// Set next task.
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(4), 10000);
				break;
			case 4:
				broadcastPacket(new SpecialCamera(_antharas.getObjectId(), 3700, 0, -3, 0, 10000, 0, 0, 1, 0));
				// Set next task.
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(5), 200);
				break;
				
			case 5:
				// Do social.
				broadcastPacket(new SpecialCamera(_antharas.getObjectId(), 1100, 0, -3, 22000, 30000, 0, 0, 1, 0));
				// Set next task.
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(6), 10800);
				break;
				
			case 6:
				// Set camera.
				broadcastPacket(new SpecialCamera(_antharas.getObjectId(), 1100, 0, -3, 300, 7000, 0, 0, 1, 0));
				// Set next task.
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
				_socialTask = ThreadPoolManager.getInstance().scheduleGeneral(new AntharasSpawn(7), 1900);
				break;
				
			case 7:
				_antharas.setIsInSocialAction(false);	//[JOJO]
				_antharas.abortCast();
				
				_mobiliseTask = ThreadPoolManager.getInstance().scheduleGeneral(new SetMobilised(_antharas), 16);
				
				// Move at random.
				if (FWA_MOVEATRANDOM)
				{
					Location pos = new Location(177915, 114941, -7707, 0);
				//	Location pos = new Location(getRandom(175000, 178500), getRandom(112400, 116000), -7707, 0);
					_moveAtRandomTask = ThreadPoolManager.getInstance().scheduleGeneral(new MoveAtRandom(_antharas, pos), /*[JOJO]*/20000/*500*/);
				}
				
				if (_socialTask != null)
				{
					_socialTask.cancel(true);
					_socialTask = null;
				}
				
				Broadcast.announceToOnlinePlayers("地竜アンタラスが登場しました。");	//[JOJO]
				announce("アンタラスを" + (FWA_INACTIVITYTIME / 60000) + "分間攻撃しないでいると、巣穴に戻ってしまいます。");	//[JOJO]
				break;
			}
		}
	}
	
	protected void broadcastPacket(L2GameServerPacket mov)
	{
		if (_Zone != null)
		{
			for (L2Character characters : _Zone.getCharactersInside())
			{
				if (characters.isPlayer())
				{
					characters.sendPacket(mov);
				}
			}
		}
	}
	
	// Do spawn Behemoth or Bomber.
	private class MobsSpawn implements Runnable
	{
		public MobsSpawn()
		{
		}
		
		@Override
		public void run()
		{
			boolean isBehemoth = getRandom(100) < FWA_PERCENTOFBEHEMOTH;
			try
			{
				int mobNumber = (isBehemoth ? 2 : 3);
				// Set spawn.
				for (int i = 0; i < mobNumber; i++)
				{
					if (_monsters.size() >= FWA_MAXMOBS)
					{
						break;
					}
					int npcId;
					if (isBehemoth)
					{
						npcId = 29069;
					}
					else
					{
						npcId = getRandom(29070, 29076);
					}
					// allocates it at random in the lair of Antharas.
					int tried = 0;
					boolean notFound = true;
					int x = 175000;
					int y = 112400;
					int dt = ((_antharas.getX() - x) * (_antharas.getX() - x)) + ((_antharas.getY() - y) * (_antharas.getY() - y));
					while ((tried++ < 25) && notFound)
					{
						int rx = getRandom(175000, 179900);
						int ry = getRandom(112400, 116000);
						int rdt = ((_antharas.getX() - rx) * (_antharas.getX() - rx)) + ((_antharas.getY() - ry) * (_antharas.getY() - ry));
						if (GeoData.getInstance().canSeeTarget(_antharas.getX(), _antharas.getY(), -7704, rx, ry, -7704))
						{
							if (rdt < dt)
							{
								x = rx;
								y = ry;
								dt = rdt;
								if (rdt <= 900000)
								{
									notFound = false;
								}
							}
						}
					}
					// Do spawn.
					_monsters.add(addSpawn(npcId, x, y, -7704, 0, false, 0));
				}
			}
			catch (Exception e)
			{
				_log.warning(e.getMessage());
			}
		}
	}
	
	@Override
	public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isSummon)
	{
		switch (npc.getId())
		{
			case 29070:
			case 29071:
			case 29072:
			case 29073:
			case 29074:
			case 29075:
			case 29076:
				if ((_selfDestructionTask == null) && !npc.isDead())
				{
					_selfDestructionTask = ThreadPoolManager.getInstance().scheduleGeneral(new SelfDestructionOfBomber(npc), FWA_SELFDESTRUCTTIME);
				}
				break;
		}
		return super.onAggroRangeEnter(npc, player, isSummon);
	}
	
	// Do self destruction.
	private class SelfDestructionOfBomber implements Runnable
	{
		private final L2Npc _bomber;
		
		public SelfDestructionOfBomber(L2Npc bomber)
		{
			_bomber = bomber;
		}
		
		@Override
		public void run()
		{
			L2Skill skill = null;
			switch (_bomber.getId())
			{
				case 29070:
				case 29071:
				case 29072:
				case 29073:
				case 29074:
				case 29075:
					skill = SkillTable.getInstance().getInfo(5097, 1);
					break;
				case 29076:
					skill = SkillTable.getInstance().getInfo(5094, 1);
					break;
			}
			
			_bomber.doCast(skill);
			if (_selfDestructionTask != null)
			{
				_selfDestructionTask.cancel(false);
				_selfDestructionTask = null;
			}
		}
	}
	
	@Override
	public String onSpellFinished(L2Npc npc, L2PcInstance player, L2Skill skill)
	{
		if (npc.isInvul())
		{
			return null;
		}
		else if ((skill != null) && ((skill.getId() == 5097) || (skill.getId() == 5094)))
		{
			switch (npc.getId())
			{
				case 29070:
				case 29071:
				case 29072:
				case 29073:
				case 29074:
				case 29075:
				case 29076:
					npc.doDie(npc);
					break;
			}
		}
		return super.onSpellFinished(npc, player, skill);
	}
	
	// At end of activity time.
	protected class CheckActivity implements Runnable
	{
		@Override
		public void run()
		{
			if (_antharas == null) return;	// dead
			
			long temp = System.currentTimeMillis() - _LastAction;
			if (temp > FWA_INACTIVITYTIME)
			{
				int npcId = _antharas.getId();
				setUnspawn();
				GrandBossManager.getInstance().setBossStatus(npcId, DORMANT);
			}
			//[JOJO]-------------------------------------------------
			else if (FWA_MOVEATRANDOM && temp >= 60000)
			{
				CtrlIntention a = _antharas.getAI().getIntention();
				if (a == CtrlIntention.AI_INTENTION_ACTIVE
				 || a == CtrlIntention.AI_INTENTION_IDLE) //move at random.
						moveAtRandom();
			}
			//-------------------------------------------------------
		}
	}
	
	// Clean Antharas's lair.
	protected void setUnspawn()
	{
		cancelAllQuestTimers();
		removeAllObjects();
	}
	
	@Override
	protected void cancelAllQuestTimers()
	{
		super.cancelAllQuestTimers();
		
		// Not executed tasks is canceled.
		if (_monsterSpawnTask != null)
		{
			_monsterSpawnTask.cancel(true);
			_monsterSpawnTask = null;
		}
		if (_activityCheckTask != null)
		{
			_activityCheckTask.cancel(false);
			_activityCheckTask = null;
		}
		if (_socialTask != null)
		{
			_socialTask.cancel(true);
			_socialTask = null;
		}
		if (_mobiliseTask != null)
		{
			_mobiliseTask.cancel(true);
			_mobiliseTask = null;
		}
		if (_mobsSpawnTask != null)
		{
			_mobsSpawnTask.cancel(true);
			_mobsSpawnTask = null;
		}
		if (_selfDestructionTask != null)
		{
			_selfDestructionTask.cancel(true);
			_selfDestructionTask = null;
		}
		if (_moveAtRandomTask != null)
		{
			_moveAtRandomTask.cancel(true);
			_moveAtRandomTask = null;
		}
	}
	
	private void removeAllObjects()
	{
		// Eliminate players.
		_Zone.oustAllPlayers();
		
		// Delete monsters.
		for (L2Npc mob : _monsters)
		{
			mob.getSpawn().stopRespawn();
			mob.deleteMe();
		}
		_monsters.clear();
		
		if (_antharas != null)
		{
			_antharas.deleteMe();
			_antharas = null;
		}
		
		// Delete teleport cube.
		for (L2Npc cube : _teleportCube)
		{
			cube.getSpawn().stopRespawn();
			cube.deleteMe();
		}
		_teleportCube.clear();
	}
	
	// Do spawn teleport cube.
	protected class SpawnCube implements Runnable
	{
		@Override
		public void run()
		{
			spawnCube();
		}
	}
	protected class SetUnspawn implements Runnable
	{
		@Override
		public void run()
		{
			setUnspawn();
		}
	}
	
	// UnLock Antharas.
	private static class UnlockAntharas implements Runnable
	{
		private final int _bossId;
		
		public UnlockAntharas(int bossId)
		{
			_bossId = bossId;
		}
		
		@Override
		public void run()
		{
			GrandBossManager.getInstance().setBossStatus(_bossId, DORMANT);
			if (FWA_DOSERVEREARTHQUAKE)
			{
				for (L2PcInstance p : L2World.getInstance().getPlayers())
				{
					p.broadcastPacket(new Earthquake(185708, 114298, -8221, 20, 10));
				}
			}
		}
	}
	
	// Action is enabled the boss.
	private class SetMobilised implements Runnable
	{
		private final L2GrandBossInstance _boss;
		
		public SetMobilised(L2GrandBossInstance boss)
		{
			_boss = boss;
		}
		
		@Override
		public void run()
		{
			_boss.setIsImmobilized(false);
			
			// When it is possible to act, a social action is canceled.
			if (_socialTask != null)
			{
				_socialTask.cancel(true);
				_socialTask = null;
			}
		}
	}
	
	//[JOJO]-------------------------------------------------
	// Move at random on after Antharas appears.
	protected void moveAtRandom()
	{
		if (FWA_MOVEATRANDOM)
		{
			L2Npc npc = _antharas;
			double radius, angle;
			if (getRandom(4) == 0)
				_Zone.broadcastPacket(new SocialAction(npc, getRandom(1, 3)));
			else if ((radius = com.l2jserver.gameserver.util.Util.calculateDistance(npc.getX(), npc.getY(), npc.getZ(), 177915, 114941, -7707, true) - 2300) > 0)
			{
				_Zone.updateKnownList(npc);
				radius += 100 + getRandom(500);
				angle = Math.atan2(114941 - npc.getY(), 177915 - npc.getX());
				int x = npc.getX() + (int)(radius * Math.cos(angle));
				int y = npc.getY() + (int)(radius * Math.sin(angle));
				npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(x, y, -7707, 0));
			}
			else
			{
				_Zone.updateKnownList(npc);
				radius = getRandom(2300);
				angle = Rnd.get() * 6.283185307179586476925286766559D;
				int x = 177915 + (int)(radius * Math.cos(angle));
				int y = 114941 + (int)(radius * Math.sin(angle));
				npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(x, y, -7707, 0));
			}
		}
	}
	//-------------------------------------------------------
	private static class MoveAtRandom implements Runnable
	{
		private final L2Npc _npc;
		private final Location _pos;
		
		public MoveAtRandom(L2Npc npc, Location pos)
		{
			_npc = npc;
			_pos = pos;
		}
		
		@Override
		public void run()
		{
			_npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, _pos);
		}
	}
	
	@Override
	public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
	{
		if ((npc.getId() == 29019) || (npc.getId() == 29066) || (npc.getId() == 29067) || (npc.getId() == 29068))
		{
			_LastAction = System.currentTimeMillis();
			if (GrandBossManager.getInstance().getBossStatus(_antharas.getId()) != FIGHTING)
			{
				_Zone.oustAllPlayers();
			}
			else if (!FWA_OLDANTHARAS && (_mobsSpawnTask == null))
			{
				startMinionSpawns(npc.getId());
			}
		}
		else if ((npc.getId() > 29069) && (npc.getId() < 29077) && (npc.getCurrentHp() <= damage))
		{
			L2Skill skill = null;
			switch (npc.getId())
			{
				case 29070:
				case 29071:
				case 29072:
				case 29073:
				case 29074:
				case 29075:
					skill = SkillTable.getInstance().getInfo(5097, 1);
					break;
				case 29076:
					skill = SkillTable.getInstance().getInfo(5094, 1);
					break;
			}
			
			npc.doCast(skill);
		}
		return super.onAttack(npc, attacker, damage, isSummon);
	}
	
	@Override
	public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
	{
		final int npcId = npc.getId();
		if (npcId == 29019 || npcId == 29066 || npcId == 29067 || npcId == 29068)
		{
			cancelAllQuestTimers();	//全タイマー停止(最優先事項)
			_antharas = null;
			_Zone.broadcastPacket(new SpecialCamera(npc.getObjectId(), 1200, 20, -10, 0, 13000, 0, 0, 1, 0));
			_Zone.broadcastPacket(new PlaySound(1, "BS01_D", 1, npc.getObjectId(), npc.getX(), npc.getY(), npc.getZ()));
			
			GrandBossManager.getInstance().setBossStatus(npcId, DEAD);
			// Calculate Min and Max respawn times randomly.
			long respawnTime = Config.ANTHARAS_SPAWN_INTERVAL + getRandom(-Config.ANTHARAS_SPAWN_RANDOM, Config.ANTHARAS_SPAWN_RANDOM);
			respawnTime *= 3600000;
			
			ThreadPoolManager.getInstance().scheduleGeneral(new UnlockAntharas(npcId), respawnTime);
			// also save the respawn time so that the info is maintained past reboots
			StatsSet info = GrandBossManager.getInstance().getStatsSet(npcId);
			info.set("respawn_time", (System.currentTimeMillis() + respawnTime));
			GrandBossManager.getInstance().setStatsSet(npcId, info);
			
			Broadcast.announceToOnlinePlayers("地竜アンタラスが死亡しました。次の復活は "+GrandBossManager.respawnTimeFormat(info)+" ごろです。");	//[JOJO]
			giveItemParty(ANTHARAS_CIRCLET, killer, npc);	//[JOJO]
			
			announce((FWA_CLEAR_DELAY / 60000) + "分以内にアンタラスの棲処から退去してください。");	//[JOJO]
			ThreadPoolManager.getInstance().scheduleGeneral(new SpawnCube(), 10000);
			ThreadPoolManager.getInstance().scheduleGeneral(new SetUnspawn(), FWA_CLEAR_DELAY);
			//[JOJO]-------------------------------------------------
			for (L2Npc mob : _monsters)
				mob.deleteMe();
			_monsters.clear();
			//-------------------------------------------------------
			_Zone.broadcastPacket(new ExShowScreenMessage(NpcStringId.THE_EVIL_LAND_DRAGON_ANTHARAS_HAS_BEEN_DEFEATED, ExShowScreenMessage.TOP_CENTER, 30000));	//[JOJO]
		}
		else if (npcId == 29069)
		{
			int countHPHerb = getRandom(6, 18);
			int countMPHerb = getRandom(6, 18);
			for (int i = 0; i < countHPHerb; i++)
			{
				npc.dropItem(killer, 8602, 1);
			}
			for (int i = 0; i < countMPHerb; i++)
			{
				npc.dropItem(killer, 8605, 1);
			}
		}
		_monsters.remove(npc);
		return super.onKill(npc, killer, isSummon);
	}

	//[JOJO]-------------------------------------------------
	private void giveItemParty(int itemId, L2PcInstance player, L2Npc npc) //+[JOJO]
	{
		if (player != null)
		{
			for (L2PcInstance pc : this.getPartyMembers(player))
			{
				PcInventory inven = pc.getInventory();
				L2ItemInstance item;
				if ((item = inven.getItemByItemId(itemId)) == null || item.getCount() < 1)
				{
					pc.addItem("Quest", itemId, 1, npc, true);
					pc.broadcastPacket(SystemMessage.getSystemMessage(SystemMessageId.ANNOUNCEMENT_C1_PICKED_UP_S2).addCharName(pc).addItemName(itemId));
				}
			}
		}
	}

	List<L2PcInstance> getPartyMembers(L2PcInstance player)	//[JOJO]
	{
		L2Party party;
		if ((party = player.getParty()) != null)
			return party.getMembers();
		else
		{
			List<L2PcInstance> m = new ArrayList<>(1);
			m.add(player);
			return m;
		}
	}

	protected void screenMessage(String text) //+[JOJO]
	{
		_Zone.broadcastPacket(new ExShowScreenMessage(text, 10000));
	}

	protected void announce(String text) //+[JOJO]
	{
		_Zone.broadcastPacket(new CreatureSay(0, 10, "", text));
	}
	//-------------------------------------------------------
	
	public static void main(String[] args)
	{
		new Antharas();
	}
}