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

import java.util.logging.Level;

import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.Location;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.interfaces.ILocational;
import com.l2jserver.gameserver.scripting.scriptengine.impl.L2Script;
import com.l2jserver.util.Rnd;

/**
 * @author FBIagent, janiko, FinalDestination, lion
 */
public final class AltarsOfSacrifice extends L2Script
{
	private final class Altar
	{
		private final ILocational _middlePoint;
		private final int[] _bossNpcIds;
		private L2Npc _spawnedBoss;
		
		protected Altar(final ILocational middlePoint, final int... bossNpcIds)
		{
			_middlePoint = middlePoint;
			_bossNpcIds = bossNpcIds;
			_spawnedBoss = null;
		}
		
		protected void spawnBoss() throws Exception
		{
			if (!hasBosses() || (_spawnedBoss != null))
			{
				throw new IllegalStateException();
			}
			
			final L2Spawn spawn = new L2Spawn(NpcTable.getInstance().getTemplate(_bossNpcIds[Rnd.get(_bossNpcIds.length)]));
			spawn.setAmount(1);
			spawn.setHeading(Rnd.get(65536));
			
			int radius = Rnd.get(BOSS_MIN_SPAWN_RADIUS, BOSS_MAX_SPAWN_RADIUS);
			double angleRadians = Rnd.get() * 2 * Math.PI;
			int spawnX = (int) (radius * Math.cos(angleRadians)) + _middlePoint.getX();
			int spawnY = (int) (radius * Math.sin(angleRadians)) + _middlePoint.getY();
			
			spawn.setXYZ(spawnX, spawnY, GeoData.getInstance().getHeight(spawnX, spawnY, _middlePoint.getZ()));
			spawn.stopRespawn();
			_spawnedBoss = spawn.spawnOne(false);
		}
		
		protected void despawnBoss()
		{
			if (_spawnedBoss != null)
			{
				_spawnedBoss.deleteMe();
				_spawnedBoss = null;
			}
		}
		
		protected void unload()
		{
			despawnBoss();
		}
		
		protected boolean hasBosses()
		{
			return _bossNpcIds.length > 0;
		}
		
		protected boolean isBossFighting()
		{
			return (_spawnedBoss != null) && _spawnedBoss.isInCombat();
		}
	}
	
	private static final String EVT_SPAWN_BOSS_PRE = "spawnboss";
	private static final String EVT_DESPAWN_BOSS_PRE = "despawnboss";
	private static final int BOSS_MIN_SPAWN_RADIUS = 250;
	private static final int BOSS_MAX_SPAWN_RADIUS = 500;
	// every 240 minutes/4 hours, altars change
	private static final long ALTAR_STATE_CHANGE_DELAY = 240 * 60 * 1000;
	
	// disabling formatter here to make this easily readable
	// @formatter:off
	private final Altar[] _altars = new Altar[]
	{
		// TalkingIsland
		new Altar
		(
			new Location(-92481, 244812, -3505)
		),
		// Elven
		new Altar
		(
			new Location(40241, 53974, -3262)
		),
		// DarkElven
		new Altar
		(
			new Location(1851, 21697, -3305),
			25750
		),
		// Dwarven
		new Altar
		(
			new Location(130133, -180968, -3271),
			25800, 25782
		),
		// Orc
		new Altar
		(
			new Location(-45329, -118327, -166),
			25779
		),
		// Kamael
		new Altar
		(
			new Location(-104031, 45059, -1417)
		),
		// Oren
		new Altar
		(
			new Location(80188, 47037, -3109),
			25767, 25770
		),
		// Gludin
		new Altar
		(
			new Location(-86620, 151536, -3018),
			25735, 25738, 25741
		),
		// Gludio
		new Altar
		(
			new Location(-14152, 120674, -2935),
			25744, 25747
		),
		// Dion
		new Altar
		(
			new Location(16715, 148320, -3210),
			25753, 25754, 25757
		),
		// Heine
		new Altar
		(
			new Location(120123, 219164, -3319),
			25773, 25776
		),
		// Giran
		new Altar
		(
			new Location(80712, 142538, -3487),
			25760, 25763, 25766
		),
		// Aden
		new Altar
		(
			new Location(152720, 24714, -2083),
			25793, 25794, 25797
		),
		// Rune
		new Altar
		(
			new Location(28010, -49175, -1278)
		),
		// Goddard
		new Altar
		(
			new Location(152274, -57706, -3383),
			25787, 25790
		),
		// Schutgart
		new Altar
		(
			new Location(82066, -139418, -2220),
			25784
		),
		// Primeval
		new Altar
		(
			new Location(10998, -24068, -3603)
		),
		// Dragon Valley
		new Altar
		(
			new Location(69592, 118694, -3417)
		)
	};
	// @formatter:on
	
	public static void main(String[] args)
	{
		new AltarsOfSacrifice();
	}
	
	private AltarsOfSacrifice()
	{
		super("AltarsOfSacrifice", "ai/group_template");
		
		for (int i = 0; i < _altars.length; ++i)
		{
			if (_altars[i].hasBosses())
			{
				startQuestTimer(makeSpawnBossEvt(i), ALTAR_STATE_CHANGE_DELAY, null, null);
			}
		}
	}
	
	private String makeSpawnBossEvt(int altarIndex)
	{
		return EVT_SPAWN_BOSS_PRE + altarIndex;
	}
	
	private String makeDespawnBossEvt(int altarIndex)
	{
		return EVT_DESPAWN_BOSS_PRE + altarIndex;
	}
	
	private boolean isSpawnBossEvt(String event)
	{
		return event.startsWith(EVT_SPAWN_BOSS_PRE);
	}
	
	private boolean isDespawnBossEvt(String event)
	{
		return event.startsWith(EVT_DESPAWN_BOSS_PRE);
	}
	
	private int getSpawnBossIndex(String event)
	{
		return Integer.parseInt(event.substring(EVT_SPAWN_BOSS_PRE.length()));
	}
	
	private int getDespawnBossIndex(String event)
	{
		return Integer.parseInt(event.substring(EVT_DESPAWN_BOSS_PRE.length()));
	}
	
	@Override
	public boolean unload(boolean removeFromList)
	{
		_log.info(getClass().getSimpleName() + ": Unloading altars due to script unloading.");
		
		for (final Altar altar : _altars)
		{
			altar.unload();
		}
		
		return super.unload(removeFromList);
	}
	
	@Override
	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
	{
		if (isSpawnBossEvt(event))
		{
			final int altarIndex = getSpawnBossIndex(event);
			final Altar altar = _altars[altarIndex];
			try
			{
				altar.spawnBoss();
				startQuestTimer(makeDespawnBossEvt(altarIndex), ALTAR_STATE_CHANGE_DELAY, null, null);
			}
			catch (Exception e)
			{
				_log.log(Level.WARNING, getClass().getSimpleName() + ": Failed to spawn altar boss.", e);
				// let's try again to spawn it in 5 seconds
				startQuestTimer(event, 5000, null, null);
			}
		}
		else if (isDespawnBossEvt(event))
		{
			final int altarIndex = getDespawnBossIndex(event);
			final Altar altar = _altars[altarIndex];
			if (altar.isBossFighting())
			{
				// periodically check if the altar boss is fighting, only despawn when not fighting anymore
				startQuestTimer(event, 5000, null, null);
			}
			else
			{
				altar.despawnBoss();
				startQuestTimer(makeSpawnBossEvt(altarIndex), ALTAR_STATE_CHANGE_DELAY, null, null);
			}
		}
		
		return null;
	}
}
