/*
 * This program 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.
 * 
 * This program 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 net.sf.l2j.gameserver.model.zone.type;

import javolution.util.FastMap;
import net.sf.l2j.gameserver.GameServer;
import net.sf.l2j.gameserver.GmListTable;
import net.sf.l2j.gameserver.datatables.MapRegionTable;
import net.sf.l2j.gameserver.instancemanager.VanHalterManager;
import net.sf.l2j.gameserver.instancemanager.lastimperialtomb.LastImperialTombManager;
import net.sf.l2j.gameserver.model.L2Attackable;
import net.sf.l2j.gameserver.model.L2Character;
import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.model.actor.instance.L2PlayableInstance;
import net.sf.l2j.gameserver.model.zone.L2ZoneType;
import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
import net.sf.l2j.util.L2FastList;

/**
 * @author DaRkRaGe
 */
public class L2BossZone extends L2ZoneType
{
	private String _zoneName;
	private int _timeInvade;
	private boolean _enabled = true; // default value, unless overridden by xml...
	
	// L2J_JP ADD SANDMAN START
	private int _InvadeTimeAfterRestart = 0;
	private boolean _IsFlyingEnable = false;
	private String _QuestName = null;
	// L2J_JP ADD SANDMAN END
	private boolean _storeToDb = true;	//+[JOJO]
	
	// track the times that players got disconnected. Players are allowed
	// to log back into the zone as long as their log-out was within _timeInvade
	// time...
	// <player objectId, expiration time in milliseconds>
	private FastMap<Integer, Long> _playerAllowedReEntryTimes;
	
	// track the players admitted to the zone who should be allowed back in 
	// after reboot/server downtime (outside of their control), within 30 
	// of server restart
	private L2FastList<Integer> _playersAllowed;
	private int[] _oustLoc =
	{
		0, 0, 0
	};
	protected L2FastList<L2Character> _raidList= new L2FastList<L2Character>();
	
	public L2BossZone(int id)
	{
		super(id);
		_playerAllowedReEntryTimes = new FastMap<Integer, Long>();
		_playersAllowed = new L2FastList<Integer>();
		_oustLoc = new int[3];
	}
	
	@Override
	public void setParameter(String name, String value)
	{
		if (name.equals("name"))
		{
			_zoneName = value;
		}
		
		else if (name.equals("InvadeTime"))
		{
			_timeInvade = Integer.parseInt(value);
		}
		else if (name.equals("InvadeTimeAfterRestart"))	// L2J_JP ADD SANDMAN
		{
			_InvadeTimeAfterRestart = Integer.parseInt(value);
		}
		else if (name.equals("Flying"))	// L2J_JP ADD SANDMAN
		{
			_IsFlyingEnable = Boolean.parseBoolean(value);
		}
		else if (name.equals("QuestName"))	// L2J_JP ADD SANDMAN
		{
			_QuestName = value;
		}
		else if (name.equals("StoreToDb"))	//+[JOJO]
		{
			_storeToDb = Boolean.parseBoolean(value);
		}
		else if (name.equals("EnabledByDefault"))
		{
			_enabled = Boolean.parseBoolean(value);
		}
		else if (name.equals("oustX"))
		{
			_oustLoc[0] = Integer.parseInt(value);
		}
		else if (name.equals("oustY"))
		{
			_oustLoc[1] = Integer.parseInt(value);
		}
		else if (name.equals("oustZ"))
		{
			_oustLoc[2] = Integer.parseInt(value);
		}
		else
		{
			super.setParameter(name, value);
		}
	}
	
	@Override
	/**
	 * Boss zones have special behaviors for player characters. Players are
	 * automatically teleported out when the attempt to enter these zones,
	 * except if the time at which they enter the zone is prior to the entry
	 * expiration time set for that player. Entry expiration times are set by
	 * any one of the following: 1) A player logs out while in a zone
	 * (Expiration gets set to logoutTime + _timeInvade) 2) An external source
	 * (such as a quest or AI of NPC) set up the player for entry.
	 * 
	 * There exists one more case in which the player will be allowed to enter.
	 * That is if the server recently rebooted (boot-up time more recent than
	 * currentTime - _timeInvade) AND the player was in the zone prior to reboot.
	 */
	protected void onEnter(L2Character character)
	{
//		// L2J_JP ADD SANDMAN
//		if (Config.USE_JP_RULE_OF_BOSSZONE)
//		{
//			this.jpOnEnter(character);
//			return;
//		}

		//〓仮〓
		if (character instanceof L2PcInstance)
		{
			if (_zoneName.equals("LastImperialTomb")
			 || _zoneName.equals("Lair of Frintezza"))				//〓DEBUG〓
				;
			if (_zoneName.equals("HallofFrintezza")) {
				;
				LastImperialTombManager.getInstance().setReachToHall();
//-				allowPlayerEntry((L2PcInstance)character, 30);
			}
			else if (_zoneName.equals("Altar of Sacrifice"))			// L2J版    zone.xml id=12014 〓TODO:動作確認[未]〓
//			else if (_zoneName.equalsIgnoreCase("AltarofSacrifice"))	// L2J_JP版 zone.xml id=12014
			{
				;
				VanHalterManager.getInstance().intruderDetection((L2PcInstance)character);
			}
		}
		//〓仮〓

		if (_enabled)
		{
			if (character instanceof L2PcInstance)
			{
				//Thread.dumpStack();
				character.setInsideZone(L2Character.ZONE_NOSUMMONFRIEND, true);
				L2PcInstance player = (L2PcInstance) character;
				if (player.isGM())
					player.sendMessage("You entered " + _zoneName);
				if (player.isMG())
					return;
				// if player has been (previously) cleared by npc/ai for entry and the zone is 
				// set to receive players (aka not waiting for boss to respawn)
				;
				if (_playersAllowed.contains(character.getObjectId()))
				{
					// Get the information about this player's last logout-exit from
					// this zone.
					Long expirationTime = _playerAllowedReEntryTimes.get(character.getObjectId());
					
					// with legal entries, do nothing.
					;
					if (expirationTime == null) // legal null expirationTime entries
					{
						long serverStartTime = GameServer.dateTimeServerStarted.getTimeInMillis();
						//long playerLastAccess = player.getLastAccess();
						;
						;
						;
						;
						if ((serverStartTime > (System.currentTimeMillis() - _timeInvade)))
							//&& (playerLastAccess < serverStartTime)
							//&& (playerLastAccess > (serverStartTime - 4 * _timeInvade)))
							return;
					}
					else
					{
						// legal non-null logoutTime entries
						_playerAllowedReEntryTimes.remove(character.getObjectId());
						;
						;
						;
						if (expirationTime.longValue() > System.currentTimeMillis())
							return;
					}
					;
					_playersAllowed.remove(_playersAllowed.indexOf(character.getObjectId()));
				}
				// teleport out all players who attempt "illegal" (re-)entry
				;
				if (_oustLoc[0] != 0 && _oustLoc[1] != 0 && _oustLoc[2] != 0)
					player.teleToLocation(_oustLoc[0], _oustLoc[1], _oustLoc[2]);
				else
					player.teleToLocation(MapRegionTable.TeleportWhereType.Town);
			}
		}
	}
	
	@Override
	protected void onExit(L2Character character)
	{
//		// L2J_JP ADD SANDMAN
//		if (Config.USE_JP_RULE_OF_BOSSZONE)
//		{
//			this.jpOnExit(character);
//			return;
//		}
		
		if (_enabled)
		{
			if (character instanceof L2PcInstance)
			{
				//Thread.dumpStack();
				character.setInsideZone(L2Character.ZONE_NOSUMMONFRIEND, false);
				L2PcInstance player = (L2PcInstance) character;
				if (player.isMG())
				{
					player.sendMessage("You left " + _zoneName);
					return;
				}
				// if the player just got disconnected/logged out, store the dc
				// time so that
				// decisions can be made later about allowing or not the player
				// to log into the zone
				if (player.isOnline() == 0 && _playersAllowed.contains(character.getObjectId()))
				{
					// mark the time that the player left the zone
					_playerAllowedReEntryTimes.put(character.getObjectId(), System.currentTimeMillis() + _timeInvade);
				}
			}
			if (character instanceof L2PlayableInstance)
			{
				if (getCharactersInside() != null && getCharactersInside().size() > 0)
				{
					_raidList.clear();
					int count = 0;
					for (L2Character obj : getCharactersInside().values())
					{
						if (obj == null)
							continue;
						if (obj instanceof L2PlayableInstance)
							count++;
						else if (obj instanceof L2Attackable && obj.isRaid())
						{
							_raidList.add(obj);
						}
					}
					// if inside zone isnt any player, force all boss instance return to its spawn points
					if (count == 0 && !_raidList.isEmpty())
					{
						for (int i = 0; i < _raidList.size(); i++)
						{
							L2Attackable raid = (L2Attackable) _raidList.get(i);
							if (!raid.isInsideRadius(raid.getSpawn().getLocx(), raid.getSpawn().getLocy(), 150, false))
								raid.returnHome();
						}
					}
				}
			}
			if (character instanceof L2Attackable && character.isRaid())
			{
				((L2Attackable) character).returnHome();
			}
		}
	}
	
	@Deprecated public boolean isZoneEnabled() {return _enabled;}	//〓仮〓
	public void setZoneEnabled(boolean flag)
	{
		if (_enabled != flag)
			oustAllPlayers();
		
		_enabled = flag;
	}
	
	public String getZoneName()
	{
		return _zoneName;
	}
	
	public int getTimeInvade()
	{
		return _timeInvade;
	}
	
	public void setAllowedPlayers(L2FastList<Integer> players)
	{
		if (players != null)
			_playersAllowed = players;
	}
	
	public L2FastList<Integer> getAllowedPlayers()
	{
		return _playersAllowed;
	}
	
	public boolean isPlayerAllowed(L2PcInstance player)
	{
		if (player.isMG())
			return true;
		else if (_playersAllowed.contains(player.getObjectId()))
			return true;
		else
		{
			if (_oustLoc[0] != 0 && _oustLoc[1] != 0 && _oustLoc[2] != 0)
				player.teleToLocation(_oustLoc[0], _oustLoc[1], _oustLoc[2]);
			else
				player.teleToLocation(MapRegionTable.TeleportWhereType.Town);
			return false;
		}
	}
	
	/**
	 * Some GrandBosses send all players in zone to a specific part of the zone,
	 * rather than just removing them all. If this is the case, this command should
	 * be used. If this is no the case, then use oustAllPlayers().
	 * 
	 * @param x
	 * @param y
	 * @param z
	 */
	
	public void movePlayersTo(int x, int y, int z)
	{
		if (_characterList == null)
			return;
		if (_characterList.isEmpty())
			return;
		for (L2Character character : _characterList.values())
		{
			if (character == null)
				continue;
			if (character instanceof L2PcInstance)
			{
				L2PcInstance player = (L2PcInstance) character;
				if (player.isOnline() == 1)
					player.teleToLocation(x, y, z);
			}
		}
	}
	
	/**
	 * Occasionally, all players need to be sent out of the zone (for example,
	 * if the players are just running around without fighting for too long, or
	 * if all players die, etc). This call sends all online players to town and
	 * marks offline players to be teleported (by clearing their relog
	 * expiration times) when they log back in (no real need for off-line
	 * teleport).
	 */
	public void oustAllPlayers()
	{
		if (_characterList == null)
			return;
		if (_characterList.isEmpty())
			return;
		for (L2Character character : _characterList.values())
		{
			if (character == null)
				continue;
			if (character instanceof L2PcInstance)
			{
				L2PcInstance player = (L2PcInstance) character;
				if (player.isOnline() == 1)
					if (_oustLoc[0] != 0 && _oustLoc[1] != 0 && _oustLoc[2] != 0)
						player.teleToLocation(_oustLoc[0], _oustLoc[1], _oustLoc[2]);
					else
						player.teleToLocation(MapRegionTable.TeleportWhereType.Town);
			}
		}
		_playerAllowedReEntryTimes.clear();
		_playersAllowed.clear();
	}
	
	/**
	 * This function is to be used by external sources, such as quests and AI
	 * in order to allow a player for entry into the zone for some time.  Naturally
	 * if the player does not enter within the allowed time, he/she will be
	 * teleported out again... 
	 * @param player: reference to the player we wish to allow 
	 * @param durationInSec: amount of time in seconds during which entry is valid.
	 */
	public void allowPlayerEntry(L2PcInstance player, int durationInSec)
	{
		if (!player.isMG())
		{
			_playersAllowed.add(player.getObjectId());
			_playerAllowedReEntryTimes.put(player.getObjectId(), System.currentTimeMillis() + durationInSec * 1000);
			
		}
	}
	
	@Override
	public void onDieInside(L2Character character)
	{
	}
	
	@Override
	public void onReviveInside(L2Character character)
	{
	}

	// L2J_JP ADD SANDMAN START
	public int getInvadeTimeAfterRestart()
	{
		return _InvadeTimeAfterRestart;
	}

	public boolean isFlyingEnable()
	{
		return _IsFlyingEnable;
	}

	public String getQuestName()
	{
		return _QuestName;
	}

	@Deprecated
	protected void jpOnEnter(L2Character character)
	{
		if (character instanceof L2PcInstance)
		{
			L2PcInstance player = (L2PcInstance) character;
			
			if (player.isGM())
			{
				player.sendMessage("[DEBUG] Into L2BossZone.jpOnEnter.");
				player.sendMessage("[DEBUG] You entered " + _zoneName);
				
				if (_QuestName != null)
				{
					if (player.getQuestState(_QuestName) == null)
					{
						player.sendMessage("[DEBUG] You dont have QuestState of  "+ _QuestName);
					}
					else
					{
						player.sendMessage("[DEBUG] You have QuestState of  "+ _QuestName);
					}
				}
			}
			
			// When the player invades the flight prohibition zone.
			// player is banished.
			if (!player.isMG() && player.isFlying() && !player.isInJail() && !_IsFlyingEnable)
			{
				player.teleToLocation(MapRegionTable.TeleportWhereType.Town);
				return;
			}
			
			// Jail a player that tries an illegal entry.
			if (!player.isMG() && _QuestName != null && player.getQuestState(_QuestName) == null)
			{
				player.sendMessage("You tried illegal entry!");
				player.setInJail(true,1440);	// Jail 1Day.
				return;
			}
			
			if (_zoneName.equals("Altar of Sacrifice"))			// L2J版    zone.xml id=12014 〓TODO:動作確認[未]〓
//			if (_zoneName.equalsIgnoreCase("AltarofSacrifice"))	// L2J_JP版 zone.xml id=12014
				VanHalterManager.getInstance().intruderDetection((L2PcInstance)character);
			
//			if (_zoneName.equals("Lair of Frintezza"))			//▲L2J版    zone.xml id=12005 〓TODO:動作確認[未]〓
			if (_zoneName.equalsIgnoreCase("HallofFrintezza"))	//  L2J_JP版 zone.xml id=12017
				LastImperialTombManager.getInstance().setReachToHall();
		}
	}

	@Deprecated
	protected void jpOnExit(L2Character character)
	{
		if (character instanceof L2PcInstance)
		{
			//Thread.dumpStack();
			L2PcInstance player = (L2PcInstance) character;
			if (player.isGM())
			{
				player.sendMessage("[DEBUG] Into L2BossZone.jpOnExit.");
				player.sendMessage("You left " + _zoneName);
				return;
			}
			
			if (player.isOnline() == 1 && _QuestName != null && player.getQuestState(_QuestName) != null)
			{
				player.getQuestState(_QuestName).exitQuest(true);
			}
		}
	}
	// L2J_JP ADD SANDMAN END

	//[JOJO]
	public boolean isStoreToDb()
	{
		return _storeToDb;
	}
	
	//[JOJO] these are methods that java calls to invoke scripts
	public void broadcastPacket(L2GameServerPacket packet)
	{
		if (_characterList == null)
			return;
		for (L2Character character : _characterList.values())
		{
			if (character instanceof L2PcInstance)
			{
				L2PcInstance player = (L2PcInstance)character;
				if (player.isOnline() == 1)
					player.sendPacket(packet);
			}
		}
	}
	//[JOJO] these are methods that java calls to invoke scripts
	public L2NpcInstance updateKnownList(L2NpcInstance npc)
	{
		if (_characterList == null)
			return npc;
		for (L2Character character : _characterList.values())
		{
			if (character instanceof L2PcInstance)
			{
				L2PcInstance player = (L2PcInstance)character;
				if (player.isOnline() == 1)
					npc.getKnownList().getKnownPlayers().put(player.getObjectId(), player);
				//x	npc.getKnownList().addKnownObject(player);
			}
		}
		return npc;
	}
	//[JOJO]end
}