/*
 * Copyright (C) 2004-2014 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 instances.DisciplesNecropolisPast;

import static com.l2jserver.gameserver.datatables.SkillData.*;

import java.util.ArrayList;

import jp.sf.l2j.arrayMaps.SortedIntIntArrayMap;
import quests.Q00196_SevenSignsSealOfTheEmperor.Q00196_SevenSignsSealOfTheEmperor;

import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.instancemanager.InstanceManager;
import com.l2jserver.gameserver.model.L2NpcPosition;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.instancezone.InstanceWorld;
import com.l2jserver.gameserver.model.quest.Quest;
import com.l2jserver.gameserver.model.quest.QuestState;
import com.l2jserver.gameserver.model.skills.AbnormalVisualEffect;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.NpcStringId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.network.serverpackets.NpcSay;
import com.l2jserver.gameserver.util.Util;

/**
 * Disciple's Necropolis Past instance zone.
 * @author Adry_85
 */
public final class DisciplesNecropolisPast extends Quest
{
	protected class DNPWorld extends InstanceWorld
	{
		protected final ArrayList<L2Npc> lilithGroup = new ArrayList<>();
		protected final ArrayList<L2Npc> anakimGroup = new ArrayList<>();
		protected final ArrayList<L2Npc> sealDevices = new ArrayList<>(4);
		protected long storeTime = 0;
		protected int countKill = 0;
	}
	
	// Instance
	private static final int INSTANCEID = 112;
	// NPCs
	private static final int SEAL_DEVICE = 27384;
	private static final int PROMISE_OF_MAMMON = 32585;
	private static final int SHUNAIMAN = 32586;
	private static final int LEON = 32587;
	private static final int DISCIPLES_GATEKEEPER = 32657;
	private static final int LILITH = 32715;
	private static final int LILITHS_STEWARD = 32716;
	private static final int LILITHS_ELITE = 32717;
	private static final int ANAKIM = 32718;
	private static final int ANAKIMS_GUARDIAN = 32719;
	private static final int ANAKIMS_GUARD = 32720;
	private static final int ANAKIMS_EXECUTOR = 32721;
	// Doors
	private static final int DOOR_1 = 17240102;
	private static final int DOOR_2 = 17240104;
	private static final int DOOR_3 = 17240106;
	private static final int DOOR_4 = 17240108;
	private static final int DOOR_5 = 17240110;
	private static final int DISCIPLES_NECROPOLIS_DOOR = 17240111;
	// Items
	private static final int SACRED_SWORD_OF_EINHASAD = 15310;
	private static final int SEAL_OF_BINDING = 13846;
	// Locations
	private static final Location ENTER = new Location(-89554, 216078, -7488, 0, 0);
	private static final Location EXIT = new Location(171895, -17501, -4903, 0, 0);
	// Monsters
	private static final int LILIM_BUTCHER = 27371;
	private static final int LILIM_MAGUS = 27372;
	private static final int LILIM_KNIGHT_ERRANT = 27373;
	private static final int SHILENS_EVIL_THOUGHTS1 = 27374;
	private static final int SHILENS_EVIL_THOUGHTS2 = 27375;
	private static final int LILIM_KNIGHT = 27376;
	private static final int LILIM_SLAYER = 27377;
	private static final int LILIM_GREAT_MAGUS = 27378;
	private static final int LILIM_GUARD_KNIGHT = 27379;
	// NpcStringId
	private static final NpcStringId[] LILITH_SHOUT =
	{
		NpcStringId.HOW_DARE_YOU_TRY_TO_CONTEND_AGAINST_ME_IN_STRENGTH_RIDICULOUS,
		NpcStringId.ANAKIM_IN_THE_NAME_OF_GREAT_SHILIEN_I_WILL_CUT_YOUR_THROAT,
		NpcStringId.YOU_CANNOT_BE_THE_MATCH_OF_LILITH_ILL_TEACH_YOU_A_LESSON
	};
	
	// Bosses Spawn
	private static final L2NpcPosition[] LILITH_SPAWN =
	{
		new L2NpcPosition(LILITH, -83175, 217021, -7504, 49151),
		new L2NpcPosition(LILITHS_STEWARD, -83327, 216938, -7492, 50768),
		new L2NpcPosition(LILITHS_ELITE, -83003, 216909, -7492, 4827),
	};
	private static final L2NpcPosition[] ANAKIM_SPAWN =
	{
		new L2NpcPosition(ANAKIM, -83179, 216479, -7504, 16384),
		new L2NpcPosition(ANAKIMS_GUARDIAN, -83321, 216507, -7492, 16166),
		new L2NpcPosition(ANAKIMS_GUARD, -83086, 216519, -7495, 15910),
		new L2NpcPosition(ANAKIMS_EXECUTOR, -83031, 216604, -7492, 17071),
	};
	
	// Skills
	private static final int SEAL_ISOLATION = getSkillHashCode(5980, 3);
	private static final SortedIntIntArrayMap SKILLS = new SortedIntIntArrayMap()
		.append(32715, getSkillHashCode(6187, 1)) // Presentation - Lilith Battle
		.append(32716, getSkillHashCode(6188, 1)) // Presentation - Lilith's Steward Battle1
		.append(32717, getSkillHashCode(6190, 1)) // Presentation - Lilith's Bodyguards Battle1
		.append(32718, getSkillHashCode(6191, 1)) // Presentation - Anakim Battle
		.append(32719, getSkillHashCode(6192, 1)) // Presentation - Anakim's Guardian Battle1
		.append(32720, getSkillHashCode(6194, 1)) // Presentation - Anakim's Guard Battle
		.append(32721, getSkillHashCode(6195, 1)) // Presentation - Anakim's Executor Battle
	;
	// Effects
	private AbnormalVisualEffect SEAL_DEVICE_ACTIVATE_EFFECT = AbnormalVisualEffect.MAGIC_SQUARE;	//[JOJO]仮
	
	private DisciplesNecropolisPast()
	{
		super(-1, DisciplesNecropolisPast.class.getSimpleName(), "instances");
		addAttackId(SEAL_DEVICE);
		addFirstTalkId(SHUNAIMAN, LEON, DISCIPLES_GATEKEEPER);
		addKillId(LILIM_BUTCHER, LILIM_MAGUS, LILIM_KNIGHT_ERRANT, LILIM_KNIGHT, SHILENS_EVIL_THOUGHTS1, SHILENS_EVIL_THOUGHTS2, LILIM_SLAYER, LILIM_GREAT_MAGUS, LILIM_GUARD_KNIGHT);
		addAggroRangeEnterId(LILIM_BUTCHER, LILIM_MAGUS, LILIM_KNIGHT_ERRANT, LILIM_KNIGHT, /*SHILENS_EVIL_THOUGHTS1, SHILENS_EVIL_THOUGHTS2,*/ LILIM_SLAYER, LILIM_GREAT_MAGUS, LILIM_GUARD_KNIGHT);
		addSpawnId(SEAL_DEVICE);
		addStartNpc(PROMISE_OF_MAMMON);
		addTalkId(PROMISE_OF_MAMMON, SHUNAIMAN, LEON, DISCIPLES_GATEKEEPER);
	}
	
	protected void spawnNPC(DNPWorld world)
	{
		final int instanceId = world.getInstanceId();
		for (L2NpcPosition p : LILITH_SPAWN)
			world.lilithGroup.add(addSpawn(p.npcId, p, false, 0, false, instanceId));
		for (L2NpcPosition p : ANAKIM_SPAWN)
			world.anakimGroup.add(addSpawn(p.npcId, p, false, 0, false, instanceId));
	}
	
	private void checkDoors(int instanceId, int countKill)
	{
		switch (countKill)
		{
			case 4:		// 4
				openDoor(DOOR_1, instanceId);
				break;
			case 10:	// 6
				openDoor(DOOR_2, instanceId);
				break;
			case 18:	// 8
				openDoor(DOOR_3, instanceId);
				break;
			case 28:	// 10
				openDoor(DOOR_4, instanceId);
				break;
			case 40:	// 12
				openDoor(DOOR_5, instanceId);
				break;
		}
	}
	
	protected int enterInstance(L2PcInstance player, String template, Location loc)
	{
		// check for existing instances for this player
		InstanceWorld world = InstanceManager.getInstance().getPlayerWorld(player);
		// existing instance
		if (world != null)
		{
			if (!(world instanceof DNPWorld))
			{
				player.sendPacket(SystemMessageId.ALREADY_ENTERED_ANOTHER_INSTANCE_CANT_ENTER);
				return 0;
			}
			teleportPlayer(player, loc, world.getInstanceId());
			removeBuffs(player);
			return world.getInstanceId();
		}
		// New instance
		world = new DNPWorld();
		world.setInstanceId(InstanceManager.getInstance().createDynamicInstance(template));
		world.setTemplateId(INSTANCEID);
		world.setStatus(0);
		((DNPWorld) world).storeTime = System.currentTimeMillis();
		InstanceManager.getInstance().addWorld(world);
		_log.info("Disciple's Necropolis Past started " + template + " Instance: " + world.getInstanceId() + " created by player: " + player.getName());
		// teleport players
		teleportPlayer(player, loc, world.getInstanceId());
		spawnNPC((DNPWorld) world);
		world.addAllowed(player.getObjectId());
		return world.getInstanceId();
	}
	
	private void makeCast(L2Npc npc, ArrayList<L2Npc> targets)
	{
		npc.setTarget(targets.get(getRandom(targets.size())));
		final Skill skill = getSkill(SKILLS.get(npc.getId()));
		if (skill != null)
			npc.doCast(skill);
	}
	
	@Override
	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
	{
		InstanceWorld tmpworld = InstanceManager.getInstance().getPlayerWorld(player);
		if (tmpworld instanceof DNPWorld)
		{
			DNPWorld world = (DNPWorld) tmpworld;
			switch (event)
			{
				case "FINISH":
				{
					cancelQuestTimer("FIGHT", null, player);
					
					player.setTarget(null);
					player.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
					
					for (L2Object obj : player.getKnownList().getKnownObjects().values())
					{
						final L2Attackable monster;
						if (obj instanceof L2Attackable && (monster = (L2Attackable) obj).getTarget() == player && monster.isInCombat())
						{
							monster.setTarget(null);
							monster.clearAggroList();
							monster.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
						}
					}
					
					for (L2Npc mob : world.sealDevices)
						mob.stopAbnormalVisualEffect(true, SEAL_DEVICE_ACTIVATE_EFFECT);
					player.showQuestMovie(13);
					startQuestTimer("TELEPORT", 27000, null, player);
					break;
				}
				case "TELEPORT":
				{
					player.teleToLocation(ENTER, 0);
					break;
				}
				case "FIGHT":
				{
					for (L2Npc caster : world.lilithGroup)
					{
						if (!caster.isCastingNow())
						{
							makeCast(caster, world.anakimGroup);
						}
						if (caster.getId() == LILITH)
						{
							if (caster.compareAndSetScriptValue(0, 1))
							{
								caster.broadcastPacket(new NpcSay(caster.getObjectId(), Say2.NPC_SHOUT, caster.getId(), NpcStringId.YOU_SUCH_A_FOOL_THE_VICTORY_OVER_THIS_WAR_BELONGS_TO_SHILIEN));
							}
							else if (getRandom(100) < 10)
							{
								caster.broadcastPacket(new NpcSay(caster.getObjectId(), Say2.NPC_SHOUT, caster.getId(), LILITH_SHOUT[getRandom(3)]));
							}
						}
					}
					for (L2Npc caster : world.anakimGroup)
					{
						if (!caster.isCastingNow())
						{
							makeCast(caster, world.lilithGroup);
						}
						if (caster.getId() == ANAKIM)
						{
							if (caster.compareAndSetScriptValue(0, 1))
							{
								caster.broadcastPacket(new NpcSay(caster.getObjectId(), Say2.NPC_SHOUT, caster.getId(), NpcStringId.FOR_THE_ETERNITY_OF_EINHASAD));
								if (Util.checkIfInRange(2000, caster, player, true))
								{
									player.sendPacket(new NpcSay(caster.getObjectId(), Say2.TELL, caster.getId(), NpcStringId.MY_POWERS_WEAKENING_HURRY_AND_TURN_ON_THE_SEALING_DEVICE));
								}
							}
							else if (getRandom(100) < 10)
							{
								switch (getRandom(3))
								{
									case 0:
									{
										caster.broadcastPacket(new NpcSay(caster.getObjectId(), Say2.NPC_SHOUT, caster.getId(), NpcStringId.DEAR_SHILLIENS_OFFSPRINGS_YOU_ARE_NOT_CAPABLE_OF_CONFRONTING_US));
										if (Util.checkIfInRange(2000, caster, player, true))
										{
											player.sendPacket(new NpcSay(caster.getObjectId(), Say2.TELL, caster.getId(), NpcStringId.ALL_4_SEALING_DEVICES_MUST_BE_TURNED_ON));
										}
										break;
									}
									case 1:
									{
										caster.broadcastPacket(new NpcSay(caster.getObjectId(), Say2.NPC_SHOUT, caster.getId(), NpcStringId.ILL_SHOW_YOU_THE_REAL_POWER_OF_EINHASAD));
										if (Util.checkIfInRange(2000, caster, player, true))
										{
											player.sendPacket(new NpcSay(caster.getObjectId(), Say2.TELL, caster.getId(), NpcStringId.LILITHS_ATTACK_IS_GETTING_STRONGER_GO_AHEAD_AND_TURN_IT_ON));
										}
										break;
									}
									case 2:
									{
										caster.broadcastPacket(new NpcSay(caster.getObjectId(), Say2.NPC_SHOUT, caster.getId(), NpcStringId.DEAR_MILITARY_FORCE_OF_LIGHT_GO_DESTROY_THE_OFFSPRINGS_OF_SHILLIEN));
										if (Util.checkIfInRange(2000, caster, player, true))
										{
											player.sendPacket(new NpcSay(caster.getObjectId(), Say2.TELL, caster.getId(), NpcStringId.DEAR_S1_GIVE_ME_MORE_STRENGTH).addPcName(player));
										}
										break;
									}
								}
							}
						}
					}
					break;
				}
			}
		}
		return super.onAdvEvent(event, npc, player);
	}
	
	@Override
	public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isSummon)
	{
		// LILIM_BUTCHER, LILIM_MAGUS, LILIM_KNIGHT_ERRANT, LILIM_KNIGHT, /*SHILENS_EVIL_THOUGHTS1, SHILENS_EVIL_THOUGHTS2,*/ LILIM_SLAYER, LILIM_GREAT_MAGUS, LILIM_GUARD_KNIGHT
		switch (npc.getId())
		{
			case LILIM_BUTCHER:
			case LILIM_GUARD_KNIGHT:
			{
				if (npc.compareAndSetScriptValue(0, 1))
				{
					npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.THIS_PLACE_ONCE_BELONGED_TO_LORD_SHILEN));
				}
				break;
			}
			case LILIM_MAGUS:
			case LILIM_GREAT_MAGUS:
			{
				if (npc.compareAndSetScriptValue(0, 1))
				{
					npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.WHO_DARES_ENTER_THIS_PLACE));
				}
				break;
			}
			case LILIM_KNIGHT_ERRANT:
			case LILIM_KNIGHT:
			{
				if (npc.compareAndSetScriptValue(0, 1))
				{
					npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.THOSE_WHO_ARE_AFRAID_SHOULD_GET_AWAY_AND_THOSE_WHO_ARE_BRAVE_SHOULD_FIGHT));
				}
				break;
			}
			case LILIM_SLAYER:
			{
				if (npc.compareAndSetScriptValue(0, 1))
				{
					npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.LEAVE_NOW));
				}
				break;
			}
		}
		return super.onAggroRangeEnter(npc, player, isSummon);
	}
	
	@Override
	public String onAttack(L2Npc npc, L2PcInstance player, int damage, boolean isSummon)
	{
		// SEAL_DEVICE x4
		InstanceWorld tmpworld = InstanceManager.getInstance().getPlayerWorld(player);
		if (tmpworld instanceof DNPWorld)
		{
			if (npc.getCurrentHp() < npc.getMaxHp() * 0.1 && npc.compareAndSetScriptValue(0, 1))
			{
				DNPWorld world = (DNPWorld) InstanceManager.getInstance().getWorld(npc.getInstanceId());
				giveItems(player, SEAL_OF_BINDING, 1);
				player.sendPacket(SystemMessageId.THE_SEALING_DEVICE_ACTIVATION_COMPLETE);
				npc.startAbnormalVisualEffect(true, SEAL_DEVICE_ACTIVATE_EFFECT);
				world.sealDevices.add(npc);
				if (world.sealDevices.size() == 4)
					startQuestTimer("FINISH", 1000, null, player);
			}
			if (getRandom(100) < 50)
			{
				npc.doCast(getSkill(SEAL_ISOLATION));
			}
		}
		return super.onAttack(npc, player, damage, isSummon);
	}
	
	@Override
	public String onFirstTalk(L2Npc npc, L2PcInstance player)
	{
		switch (npc.getId())
		{
			case SHUNAIMAN:
			{
				return "32586.htm";
			}
			case LEON:
			{
				return "32587.htm";
			}
			case DISCIPLES_GATEKEEPER:
			{
				return "32657.htm";
			}
		}
		return super.onFirstTalk(npc, player);
	}
	
	@Override
	public String onKill(L2Npc npc, L2PcInstance player, boolean isSummon)
	{
		// LILIM_BUTCHER, LILIM_MAGUS, LILIM_KNIGHT_ERRANT, LILIM_KNIGHT, SHILENS_EVIL_THOUGHTS1, SHILENS_EVIL_THOUGHTS2, LILIM_SLAYER, LILIM_GREAT_MAGUS, LILIM_GUARD_KNIGHT
		InstanceWorld tmpworld = InstanceManager.getInstance().getPlayerWorld(player);
		if (tmpworld instanceof DNPWorld)
		{
			DNPWorld world = (DNPWorld) tmpworld;
			final int countKill; synchronized (world) { countKill = ++world.countKill; }	// atomic 槍対策：武器「15310アインハザードの聖剣」〜スキル「8357-1アインハザードの一撃」targetType=>FRONT_AREA
			checkDoors(world.getInstanceId(), countKill);
		}
		
		switch (npc.getId())
		{
			case LILIM_MAGUS:
			case LILIM_GREAT_MAGUS:
			{
				npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.LORD_SHILEN_SOME_DAY_YOU_WILL_ACCOMPLISH_THIS_MISSION));
				break;
			}
			case LILIM_KNIGHT_ERRANT:
			case LILIM_KNIGHT:
			case LILIM_GUARD_KNIGHT:
			{
				npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.WHY_ARE_YOU_GETTING_IN_OUR_WAY));
				break;
			}
			case LILIM_SLAYER:
			{
				npc.broadcastPacket(new NpcSay(npc, Say2.NPC_ALL, NpcStringId.FOR_SHILEN));
				break;
			}
		}
		return super.onKill(npc, player, isSummon);
	}
	
	@Override
	public final String onSpawn(L2Npc npc)
	{
		// SEAL_DEVICE
		npc.setIsMortal(false);
		return super.onSpawn(npc);
	}
	
	@Override
	public String onTalk(L2Npc npc, L2PcInstance talker)
	{
		final QuestState qs = talker.getQuestState(Q00196_SevenSignsSealOfTheEmperor.class.getSimpleName());
		String htmltext = getNoQuestMsg(talker);
		if (qs == null)
		{
			return htmltext;
		}
		
		switch (npc.getId())
		{
			case PROMISE_OF_MAMMON:
			{
				if (qs.isCond(3) || qs.isCond(4))
				{
					enterInstance(talker, "DisciplesNecropolisPast.xml", ENTER);
					return "";
				}
				break;
			}
			case LEON:
			{
				if (qs.getCond() >= 3)
				{
					takeItems(talker, SACRED_SWORD_OF_EINHASAD, -1);
					InstanceWorld world = InstanceManager.getInstance().getPlayerWorld(talker);
					world.removeAllowed(talker.getObjectId());
					talker.teleToLocation(EXIT, 0);
					htmltext = "32587-01.html";
				}
				break;
			}
			case DISCIPLES_GATEKEEPER:
			{
				if (qs.getCond() >= 3)
				{
					InstanceWorld tmpworld = InstanceManager.getInstance().getWorld(npc.getInstanceId());
					if (tmpworld instanceof DNPWorld)
					{
						DNPWorld world = (DNPWorld) tmpworld;
						openDoor(DISCIPLES_NECROPOLIS_DOOR, world.getInstanceId());
						talker.showQuestMovie(12);
						startQuestTimer("FIGHT", 1000, null, talker, Quest.Repeating.WithFixedDelay);
						htmltext = null;
					}
				}
				break;
			}
		}
		return htmltext;
	}
	
	private static final void removeBuffs(L2Character ch)
	{
		ch.stopAllEffectsExceptThoseThatLastThroughDeath();
		if (ch.hasSummon())
		{
			ch.getSummon().stopAllEffectsExceptThoseThatLastThroughDeath();
		}
	}
	
	public static void main(String[] args)
	{
		new DisciplesNecropolisPast();
	}
}
