/*
 * 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;

import java.util.Collection;
import java.util.Set;

import ai.npc.AbstractNpcAI;

import com.l2jserver.gameserver.ai.CtrlEvent;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.ai.NextAction;
import com.l2jserver.gameserver.datatables.DoorTable;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.idfactory.IdFactory;
import com.l2jserver.gameserver.instancemanager.CastleManager;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.instancemanager.WalkingManager;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.L2World;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2RaidBossInstance;
import com.l2jserver.gameserver.model.entity.GrandBossState.StateEnum;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.network.NpcStringId;
import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo.NpcInfo;
import com.l2jserver.gameserver.network.serverpackets.NpcSay;
import com.l2jserver.gameserver.network.serverpackets.SocialAction;
import com.l2jserver.util.Rnd;

/**
 * Benom AI
 * @author theOne
 *      "benom.py" l2jfree-datapack rev6024, timestamp 2009/06/01 04:08:49
 * JOJO Translate
 *      "benom.py" to "Benom.java"
 */

public class Benom extends AbstractNpcAI
{
	private static final boolean DEBUG = true;

	private static final boolean OPTION1 = true;
					//JOJO ルール変更
					// ルウン城の城主がＮＰＣのときは、
					//   ベノム(テレポーター)が伝令の横に出現し、
					//   誰でもベノム(ボス)を討伐することができる。
					// ルウン城に城主がいるときは、
					//   ベノム(テレポーター)が玉座の間に出現し、
					//   城の血盟のみが狩ることができる。

	private static final int BENOM = 29054;
	private static final int BENOM_TELEPORT = 13101;

	private static final int BENOM_SPAWN_TIME = 86400000;	//getSiegeDate()-24H
	private static final int BENOM_DESPAWN_TIME = 5400000;	//getSiegeDate()+1.5H

	private static final NpcStringId[] BENOM_SPEAK =
	{
		NpcStringId.WHO_DARES_TO_COVET_THE_THRONE_OF_OUR_CASTLE_LEAVE_IMMEDIATELY_OR_YOU_WILL_PAY_THE_PRICE_OF_YOUR_AUDACITY_WITH_YOUR_VERY_OWN_BLOOD,
		NpcStringId.HMM_THOSE_WHO_ARE_NOT_OF_THE_BLOODLINE_ARE_COMING_THIS_WAY_TO_TAKE_OVER_THE_CASTLE_HUMPH_THE_BITTER_GRUDGES_OF_THE_DEAD_YOU_MUST_NOT_MAKE_LIGHT_OF_THEIR_POWER,
		NpcStringId.AARGH_IF_I_DIE_THEN_THE_MAGIC_FORCE_FIELD_OF_BLOOD_WILL,
	};

	private int checkState()
	{
		return GrandBossManager.getInstance().getBossStatus(BENOM);
	}

	private void updateState(int status)
	{
		if (checkState() != status)
		{
			GrandBossManager.getInstance().setBossStatus(BENOM, status);
			GrandBossManager.getInstance().setStatsSet(BENOM, null);
		}
	}

	private void enableWalk()
	{
		_benomWalkRouteStep = BENOM_WALK_ROUTES.length - 1;	//enable walk. next route is #0.
		_benomBlock = 0;
		_benom.setCanReturnToSpawnPoint(false);
		WalkingManager.getInstance().setWalker(_benom, true);

		_evtArrived = new NextAction(CtrlEvent.EVT_ARRIVED, null, null) {
			@Override protected void doWork() {
				onEvtArrived(_benom);
			}
		};
	}
	private void disableWalk()
	{
		_benomWalkRouteStep = -1;
		_benomBlock = 0;
		_benom.setCanReturnToSpawnPoint(true);
		WalkingManager.getInstance().setWalker(_benom, false);
		_evtArrived = null;
	}

	private void despawnNpc(L2Npc npc)
	{
		if (!npc.isDead()) npc.deleteMe();
		if (DEBUG) {
			SpawnTable.getInstance().deleteSpawn(npc.getSpawn(), false);
			Set<L2Spawn> spawn;
			if ((spawn = SpawnTable.getInstance().getSpawns(npc.getId())) != null && !spawn.isEmpty())
			{
				throw new AssertionError(npc.getId() + npc.getName());
			}
		}
	}

	private static class Route extends Location
	{
		public int door;
		Route(int x, int y, int z)
		{
			super(x, y, z, 0);
			this.door = 0;
		}
		Route(int x, int y, int z, int door)
		{
			super(x, y, z, 0);
			this.door = door;
		}
	}
	private static final Route[] BENOM_WALK_ROUTES = {
		/*0*/	new Route(12565,-49739, -547),
		/*1*/	new Route(11242,-49689,  -33),
		/*2*/	new Route(10751,-49702,   83),
		/*3*/	new Route(10824,-50808,  316),
		/*4*/	new Route( 9084,-50786,  972),
		/*5*/	new Route( 9095,-49787, 1252),
		/*6*/	new Route( 8371,-49711, 1252),
		/*7*/	new Route( 8423,-48545, 1252),
		/*8*/	new Route( 9105,-48474, 1252),
		/*9*/	new Route( 9085,-47488,  972),
		/*10*/	new Route(10858,-47527,  316),
		/*11*/	new Route(10842,-48626,   75),
		/*12*/	new Route(12171,-48464, -547),
		/*13*/	new Route(12580,-49145, -535),
		/*14*/	new Route(13565,-49145, -535),
		/*15*/	new Route(15290,-49160,-1066),
		/*16*/	new Route(15653,-49159,-1059, 20160005),
		/*17*/	new Route(15423,-48402, -839),
		/*18*/	new Route(15066,-47438, -419),
		/*19*/	new Route(13990,-46843, -292),
		/*20*/	new Route(13685,-47371, -163),
		/*21*/	new Route(13384,-47470, -163),
		/*22*/	new Route(14609,-48608,  346),
		/*23*/	new Route(14301,-47906,  498),
		/*24*/	new Route(13667,-47488,  743),
		/*25*/	new Route(12894,-49109,  980),
		/*26*/	new Route(10135,-49150,  996),
		/*27*/	new Route(12894,-49109,  980),
		/*28*/	new Route(13738,-50894,  747),
		/*29*/	new Route(14398,-50314,  462),
		/*30*/	new Route(14579,-49698,  347),
		/*31*/	new Route(12896,-51135, -166),
		/*32*/	new Route(12852,-51922, -295),
		/*33*/	new Route(14275,-51487, -295),
		/*34*/	new Route(15328,-50406, -603),
		/*35*/	new Route(15594,-49192,-1059),
		/*36*/	new Route(15060,-49160,-1023, 20160005),
		/*37*/	new Route(12956,-49153, -537),
	};

	protected L2RaidBossInstance _benom = null;
	private L2Npc _benomTeleport = null;
	private NextAction _evtArrived;
	private int _benomBlock;
	protected int _benomWalkRouteStep = -1;

	//[JOJO]-------------------------------------------------
	private class SpecialCamera extends com.l2jserver.gameserver.network.serverpackets.SpecialCamera
	{
		public SpecialCamera(int id, int dist, int yaw, int pitch, int time, int duration)
		{
			super(id, dist, yaw, pitch, time, duration, 0, 0, 1, 0);
		}
	}
	//-------------------------------------------------------

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

		// Quest NPC starter initialization
		addStartNpc(BENOM_TELEPORT);
		addTalkId(BENOM_TELEPORT);
		addSpawnId(BENOM);
		addAggroRangeEnterId(BENOM);
		addKillId(BENOM);

		switch (checkState()) {
		case StateEnum.DEAD:
		case StateEnum.INTERVAL:
			interval();
			break;
	/*	case StateEnum.NOTSPAWN: */
	/*	case StateEnum.ALIVE: */
		default:
    		init();
		}
	}

	/**
	 * 攻城時刻の24時間前になったらベノム(テレポとレイド)を配置するようタイマー設定
	 */
	private void init()
	{
		long now = System.currentTimeMillis();
		long siegeDate = CastleManager.getInstance().getCastleById(8).getSiegeDate().getTimeInMillis();
		if (now < siegeDate - /*24H*/BENOM_SPAWN_TIME)
		{
			long t = siegeDate - BENOM_SPAWN_TIME - now;
			updateState(StateEnum.NOTSPAWN);
			startQuestTimer("BenomRaidRoomSpawn", t, null, null);
		}
		else if (now < siegeDate + /*1.5H*/BENOM_DESPAWN_TIME)
		{
			long t = 1000;
			updateState(StateEnum.NOTSPAWN);
			startQuestTimer("BenomRaidRoomSpawn", t, null, null);
		}
		else
		{
			updateState(StateEnum.INTERVAL);
			interval();
		}
	}

	/**
	 * 攻城戦(2時間)が終了して次回の日程が確定するまで(24時間)待機する
	 */
	private void interval()
	{
		long now = System.currentTimeMillis();
		long timeRegistrationOverDate = CastleManager.getInstance().getCastleById(8).getSiege().getTimeRegistrationOverDate().getTimeInMillis();
		long siegeDate = CastleManager.getInstance().getCastleById(8).getSiegeDate().getTimeInMillis();

		if (timeRegistrationOverDate < now && now < siegeDate - /*24H*/BENOM_SPAWN_TIME)
		{
			//次回の攻城戦の日時が確定している
			updateState(StateEnum.NOTSPAWN);
			init();
			return;
		}

		//次回の攻城戦の日時が確定するまで(繰り返し)待つ
		long intervalTime = Math.max(21600000, timeRegistrationOverDate - now);
		startQuestTimer("interval", intervalTime, null, null);
	}

	private L2PcInstance getRandomPlayer(L2Npc npc)
	{
		Collection<L2PcInstance> knowns = npc.getKnownList().getKnownPlayers().values();
		if (knowns.size() <= 0)
			return null;
		L2PcInstance[] knownPlayers = new L2PcInstance[knowns.size()];
		int count = 0;
		for (L2PcInstance player : knowns)
		{
			if (player == null || player.isDead())
				continue;
			knownPlayers[count++] = player;
		}
		if (count > 0)
			return knownPlayers[Rnd.get(count)];
		else
			return null;
	}

	@Override
	public String onTalk(L2Npc npc, L2PcInstance player)
	{
		int castleOwner = CastleManager.getInstance().getCastleById(8).getOwnerId();
		int clanId = player.getClanId();
		if (castleOwner == 0 && OPTION1
		 || castleOwner != 0 && castleOwner == clanId)
		{
			int x =  12558 + Rnd.get(-100, 100);
			int y = -49279 + Rnd.get(-100, 100);
			player.teleToLocation(x, y, -3007);
			return null;
		}
		else
			return "<html><body>Benom's Avatar:<br>Your clan does not own this castle. Only members of this Castle's owning clan can challenge Benom.</body></html>";
	}

	@Override
	public String onSpawn(L2Npc npc)
	{
		if (!npc.isTeleporting())
		{
			if (DEBUG) if (_benom != null) throw new RuntimeException();
			_benom = (L2RaidBossInstance) npc;
		}
		return super.onSpawn(npc);
	}

	private void onDespawn(L2Npc npc)
	{
		cancelBenomTimers(npc);
		disableWalk();	//disable walk
		despawnNpc(npc);
		_benom = null;
	}

	@Override
	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
	{
		if (event == "BenomRaidRoomSpawn")
		{
			if (checkState() == StateEnum.DEAD)
			{
				return null;
			}

			if (_benom == null)
				_benom = (L2RaidBossInstance) addSpawn(BENOM, 12047, -49211, -3009, 0, false, 0);	//underground jail dungeon
			else
				_benom.teleToLocation(12047, -49211, -3009);

			if (CastleManager.getInstance().getCastleById(8).getOwnerId() > 0)
			{
				_benomTeleport = addSpawn(BENOM_TELEPORT, 11013, -49629, -547, 16384, false, 0);	//throne room
			}
			else
			{
				_benomTeleport = addSpawn(BENOM_TELEPORT, 26969, -49278, -1305, 0, false, 0);	//out of the castle
			}
			
			disableWalk();	//disable walk
			updateState(StateEnum.ALIVE);

			/**
			 * 攻城時刻になったらトリオルの長ベノムが地下収容所から玉座の間にテレポートするようタイマー設定
			 */
			long now = System.currentTimeMillis();
			long siegeDate = CastleManager.getInstance().getCastleById(8).getSiegeDate().getTimeInMillis();
			long t = Math.max(1000, siegeDate - now);
			startQuestTimer("BenomRaidSiegeSpawn", t, _benom, null);
		}
		else if (event == "BenomRaidSiegeSpawn")
		{
			if (_benomTeleport != null)
			{
				despawnNpc(_benomTeleport);
				_benomTeleport = null;
			}

			if (npc == null || npc.isDead() || ! npc.isVisible() || checkState() != StateEnum.ALIVE)
				return null;

			npc.teleToLocation(11025, -49152, -537);	//throne room
			startQuestTimer("BenomSpawnEffect", 100, npc, null);
			startQuestTimer("BenomBossDespawn", BENOM_DESPAWN_TIME, npc, null);
		}
		else if (event == "BenomSpawnEffect")
		{
			npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
			npc.broadcastPacket(new SpecialCamera(npc.getObjectId(), 200, 0, 150, 0, 5000));
			npc.broadcastPacket(new SocialAction(npc, 3));

			enableWalk();
			startQuestTimer("BenomWalk", 5000, npc, null);
		}
		else if (event == "Attacking")
		{
			if (npc.isDead() || ! npc.isVisible())
				return null;

			L2PcInstance target;
			if ((target = getRandomPlayer(npc)) != null)
			{
				((L2Attackable)npc).addDamageHate(target, 0, 999);
				npc.setRunning();
				npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
				startQuestTimer("Attacking", 2000, npc, null);
			}
			else if (_benomWalkRouteStep >= 0)
			{
				 startQuestTimer("BenomJump", 20000, npc, null);
			}
		}
		else if (event == "BenomJump")
		{
			if (npc.isDead() || ! npc.isVisible())
				return null;

			cancelQuestTimers("Attacking");
			Route route = BENOM_WALK_ROUTES[_benomWalkRouteStep];
			npc.teleToLocation(route.getX(), route.getY(), route.getZ());
			startQuestTimer("BenomWalk", 2200, npc, null);
		}
		else if (event == "BenomRetry")
		{
			if (npc.isDead() || ! npc.isVisible())
				return null;

			Route route = BENOM_WALK_ROUTES[_benomWalkRouteStep];
			npc.setWalking();
			npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, route);
			npc.getAI().setNextAction(_evtArrived);
		}
		else if (event == "BenomWalk")
		{
			if (npc.isDead() || ! npc.isVisible())
				return null;

			Route route = BENOM_WALK_ROUTES[_benomWalkRouteStep];
			if (route.door != 0)
				DoorTable.getInstance().getDoor(route.door).closeMe();

			if (Rnd.get(100) < 40)
				startQuestTimer("Talk", 100, npc, null);

			_benomBlock = 0;
			_benomWalkRouteStep = (_benomWalkRouteStep + 1) % BENOM_WALK_ROUTES.length;
			route = BENOM_WALK_ROUTES[_benomWalkRouteStep];
			if (route.door != 0)
				DoorTable.getInstance().getDoor(route.door).openMe();
			npc.setWalking();
			npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, route);
			npc.getAI().setNextAction(_evtArrived);
		}
		else if (event == "Talk")
		{
			npc.broadcastPacket(new NpcSay(npc.getObjectId(), 0, npc.getId(), BENOM_SPEAK[Rnd.get(BENOM_SPEAK.length)]));
		}
		else if (event == "BenomBossDespawn")
		{
			onDespawn(npc);
			updateState(StateEnum.INTERVAL);
			interval();
		}
		else if (event == "interval")
		{
			interval();
		}
		else
		{
			throw new AssertionError();
		}
		return super.onAdvEvent(event, npc, player);
	}

	public void onEvtArrived(L2Npc npc)
	{
		if (_benomWalkRouteStep >= 0 && !npc.isDead())
		{
			L2RaidBossInstance benom = (L2RaidBossInstance)npc;
			final Route route = BENOM_WALK_ROUTES[_benomWalkRouteStep];
			if (benom.isInCombat() /*|| benom.getAI().getIntention() == CtrlIntention.AI_INTENTION_ATTACK*/)
			{
				if (getQuestTimer("Attacking", benom, null) == null)
					startQuestTimer("Attacking", 2000, benom, null);
			}
			else if (npc.isInsideRadius(route.getX(), route.getY(), route.getZ(), 50, true, true))
			{
				onAdvEvent("BenomWalk", npc, null);
			}
			else if (++_benomBlock >= 2)
			{
				startQuestTimer("BenomJump", 1000, benom, null);
			}
			else
			{
				onAdvEvent("BenomRetry", benom, null);
			}
		}
	}

	@Override
	public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isPet)
	{
		cancelQuestTimers("BenomWalk");
		cancelQuestTimers("BenomJump");
		startQuestTimer("Attacking", 100, npc, null);
		return super.onAggroRangeEnter(npc, player, isPet);
	}

	@Override
	public String onKill(L2Npc npc, L2PcInstance killer, boolean isPet) 
	{
		onDespawn(npc);
		npc.broadcastPacket(new NpcSay(npc.getObjectId(), 0, npc.getId(), NpcStringId.ITS_NOT_OVER_YET_IT_WONT_BE_OVER_LIKE_THIS_NEVER));

		if (_benomTeleport != null)
		{
			despawnNpc(_benomTeleport);
			_benomTeleport = null;
		}

		updateState(StateEnum.DEAD);
		interval();
		return super.onKill(npc,killer,isPet);
	}

	private void cancelBenomTimers(L2Npc npc)
	{
		cancelQuestTimers("BenomWalk");
		cancelQuestTimers("BenomJump");
		cancelQuestTimers("BenomBossDespawn");
		cancelQuestTimers("Talk");
		cancelQuestTimers("Attacking");
	}

	/**
	 * DEBUG
	 */
	@SuppressWarnings("unused")
	private void visualizeWalkRoutes(L2PcInstance player)
	{
		final int npcId = 1032467;

		for (int e = 0; e < BENOM_WALK_ROUTES.length; e++) {
			final Route route = BENOM_WALK_ROUTES[e];
			final Route next  = BENOM_WALK_ROUTES[(e + 1) % BENOM_WALK_ROUTES.length];

			final int x1 = route.getX(), y1 = route.getY(), z1 = route.getZ();
			L2Npc n = addSpawn(npcId, x1, y1, z1, 0, false, BENOM_DESPAWN_TIME/*86400000*/);
			n.setTitle("#"+e);
			n.broadcastPacket(new NpcInfo(n, null));

			final double dx = next.getX() - x1, dy = next.getY() - y1, dz = next.getZ() - z1;
			final double distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
			final double pitch = 50;
			for (double p = pitch; p < distance; p += pitch) {
				double a = p / distance;
				int x = x1 + (int)Math.round(dx * a);
				int y = y1 + (int)Math.round(dy * a);
				int z = z1 + (int)Math.round(dz * a) + 20;
				L2ItemInstance i = new L2ItemInstance(IdFactory.getInstance().getNextId(), 57);
				if (player != null) i.setOwnerId(player.getObjectId());
				i.new ItemDropTask(i, null, x, y, z).run();
				i.setProtected(false);
				L2World.getInstance().storeObject(i);
			}
		}
	}

	public static void main(String[] args)
	{
		// now call the constructor (starts up the ai)
		new Benom(-1,"benom","ai");
	}
}
