/*
 * 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 custom.events.Rabbits;

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

import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

import jp.sf.l2j.troja.FastIntObjectMap;

import com.l2jserver.Config;
import com.l2jserver.gameserver.Announcements;
import com.l2jserver.gameserver.datatables.NpcData;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.quest.Event;
import com.l2jserver.gameserver.model.quest.QuestTimer;
import com.l2jserver.gameserver.model.skills.CommonSkill;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.network.clientpackets.Say2;
import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo;
import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jserver.gameserver.network.serverpackets.NpcSay;
import com.l2jserver.util.Rnd;

public final class Rabbits extends Event
{
	// NPCs
	private static final int NPC_MANAGER = 900101;
	private static final int CHEST = 900102;
	private static final int DUMMY_CHEST = 13097;
	// Skills
	//		629-1 ラビット マジックアイ - 心を空にして遠くを見つめると、隠された宝箱を見つけることができます。
	//		630-1 ラビット トルネード - 360度にくるりと回り、強力な蹴りを食らわせ箱を開きます。
	private static final int RABBIT_MAGIC_EYE_ID = 629;
	private static final int RABBIT_TORNADO_ID = 630;
	private static final int RABBIT_TRANSFORMATION = getSkillHashCode(2428, 1);
	private static final int RAID_CURSE = getSkillHashCode(4515, 1);
	// Misc
	private static final int EVENT_TIME = 10;
	private static final int TOTAL_CHEST_COUNT = 75;
	private static final int TRANSFORMATION_ID = 105;	//skill 2428 "Scroll of Transformation - Rabbit"
	private static final int BONUS_RATE = 20;
	private static final int MINIMAL_SOCIAL_INTERVAL = 5000;
	
	// Working-storage Section
	protected L2MonsterInstance[] _chests;
	protected L2Npc _manager;
	private L2MonsterInstance _dondon;
	private FastIntObjectMap<L2PcInstance> _players;
	private boolean _isActive = false;
	private AtomicInteger _chest_count;	// Current Chest count
	
	/**
	 * Drop data:<br>
	 * Higher the chance harder the item.<br>
	 * ItemId, chance in percent, min amount, max amount
	 */
	private static final int[][] DROPLIST =
	{
		{  1540,  20, 10, 15 },	// Quick Healing Potion
		{  1538,  20,  5, 10 },	// Blessed Scroll of Escape
		{  3936,  20,  5, 10 },	// Blessed Scroll of Ressurection
		{  6387,  15,  5, 10 },	// Blessed Scroll of Ressurection Pets
		{ 22025,  10,  5, 10 },	// Powerful Healing Potion
		{  6622,   5,  1, 1 },	// Giant's Codex
		{ 20034,   5,  1, 1 },	// Revita Pop
		{ 20004,   4,  1, 1 },	// Energy Ginseng
		{ 20004,   1,  1, 1 }	// Energy Ginseng
	};
	
	private static final int[][] BONUSLIST =
	{
		// data/extractable_skills.csv
		// data/stats/items/10200-10299.xml + <set name="handler" val="ItemSkills" />
		{ 10259, 33, 1, 1 },	// 宝物の袋：6級品 - 強化魔法ジュース
		{ 10258, 24, 1, 1 },	// 宝物の袋：5級品 - カクテル セット
		{ 10257, 18, 1, 1 },	// 宝物の袋：4級品 - Cグレード/Dグレード強化スクロール、幻影アガシオン、祝福された帰還スクロール
		{ 10273, 12, 1, 1 },	// 搭乗用ウルフ召喚ブレスレット（幻影兵器）
		{ 10256,  7, 1, 1 },	// 宝物の袋：3級品 - Aグレード/Bグレード強化スクロール、生命の石、祝福された復活スクロール
		{ 10255,  5, 1, 1 },	// 宝物の袋：2級品 - Sグレード強化スクロール、下巨人の秘伝書（忘却/修練/熟練編）、属性石
		{ 10254,  1, 1, 1 },	// 宝物の袋：1級品 - Sグレード防具、ソウル ストーン：ステップ13〜14
	};
	
	private static final int DROPLIST_TOTAL_CHANCE, BONUSLIST_TOTAL_CHANCE;
	static {
		int chance = 0;
		for (int[]i : DROPLIST)
			chance += i[1];
		DROPLIST_TOTAL_CHANCE = chance;
		chance = 0;
		for (int[]i : BONUSLIST)
			chance += i[1];
		BONUSLIST_TOTAL_CHANCE = chance;
	}
	
	protected static final int[] DONDON_SAY =
	{
		1600020,	//Startled	ドンドンッ。
		1600021,	//Bumps	ガタゴトッ。
	};
	
	private static final int[] ON_DROP_SAY =
	{
		1600006,	//You've made a great choice.	よくぞ選んでくれました！
	};
	
	private static final int[] TRIGGERE_SAY = 
	{
		1600008,	//Did you see that Firecracker explode?	私を選んで！他のはクズよ！
		1600009,	//I am nothing.	いやいやっ！私にさわらないで！
	};
	
	private static final int[] RANDAM_SAY =
	{
		1600007,	//A relaxing feeling is moving through my stomach.	私の中のブツがウズいてるぅ。
		1600010,	//I am telling the truth.	私、見た目よりすごいんです！
		1600022,	//You will regret this.	後悔しますよ。
	};
	
	private static final int[] HIDE_SAY =
	{
		1600004,	//Boo-hoo... I hate...	くすん．．．嫌い．．．
		1600005,	//See you later.	またね〜っ！
	};
	
	//-------------------------------------------------------
	
	private Rabbits()
	{
		super(Rabbits.class.getSimpleName(), "custom/events");
		if (NpcData.getInstance().getTemplate(NPC_MANAGER) == null)
		{
			_log.warning("Rabbits: L2NpcTemplate is a NullPointer -> Invalid npc id(" + NPC_MANAGER + ") in configs?");
			_log.info("Rabbits: Script is disabled.");
			return;
		}
		
		addFirstTalkId(NPC_MANAGER, CHEST);
		addTalkId(NPC_MANAGER);
		addStartNpc(NPC_MANAGER);
		addSkillSeeId(CHEST);
		addAttackId(CHEST);
		addCanSeeMeId(CHEST);
	}
	
	@Override
	public boolean eventStart(L2PcInstance eventMaker)
	{
		// Don't start event if its active
		if (_isActive)
		{
			eventMaker.sendMessage("Event " + getName() + " is already started!");
			return false;
		}
		
		// Check starting conditions
		if (!Config.CUSTOM_NPC_DATA)
		{
			_log.info(getName() + ": Event can't be started, because custom NPCs are disabled!");
			eventMaker.sendMessage("Event " + getName() + " can't be started because custom NPCs are disabled!");
			return false;
		}
		
		// Set Event active
		_isActive = true;
		
		// Initialize list
		_chests = new L2MonsterInstance[TOTAL_CHEST_COUNT];
		_players = new FastIntObjectMap<L2PcInstance>().shared();
		
		// Spawn Manager
		_manager = addSpawn(NPC_MANAGER, -59227, -56939, -2039, 64106, false, 0);
		
		// Spawn Chests
		_dondon = (L2MonsterInstance) addSpawn(DUMMY_CHEST, -59227, -56939, -2039, 64106, false, 0);
		_dondon.setIsInvul(true);
		_dondon.setCanReturnToSpawnPoint(false);
		startQuestTimer("DONDON_TASK", 5000, _dondon, null, true);
		
		for (int index = 0; index < TOTAL_CHEST_COUNT; ++index)
		{
			L2MonsterInstance chest = (L2MonsterInstance) addSpawn(CHEST, getRandom(-60653, -58772), getRandom(-55830, -57718), -2030, 0, true, EVENT_TIME * 60 * 1000);
			chest.setIsImmobilized(true);
			hideMe(chest);
			chest.disableCoreAI(true);
			_chests[index] = chest;
		}
		_chest_count = new AtomicInteger(TOTAL_CHEST_COUNT);
		
		// Announce event start
		Announcements.getInstance().announceToAll("ウサギ イベント: 宝箱出現！");
		Announcements.getInstance().announceToAll("ウサギ イベント: 幻想の島で待ってま〜す。ステキなお商品をGetしてね〜★");
		Announcements.getInstance().announceToAll("ウサギ イベント: 制限時間は" + EVENT_TIME + "分だよ！");
		Announcements.getInstance().announceToAll("ウサギ イベント: 時間が過ぎたら宝箱は全部消えま〜す");
		// Schedule event end
		startQuestTimer("END_RABBITS_EVENT", EVENT_TIME * 60000, null, eventMaker);
		return true;
	}
	
	@Override
	public boolean eventStop()
	{
		synchronized (this)
		{
			// Don't stop inactive event
			if (!_isActive)
			{
				return false;
			}
			
			// Set inactive
			_isActive = false;
		}
		
		// Cancel timer
		cancelAllQuestTimers();
		
		// Despawn NPCs
		for (L2MonsterInstance chest : _chests)
		{
			if (chest != null)
			{
				chest.deleteMe();
			}
		}
		_chests = null;
		_chest_count = null;
		
		_dondon.deleteMe();
		_dondon = null;
		
		_manager.deleteMe();
		_manager = null;
		
		for (L2PcInstance player : _players.values())
		{
			if (player.getTransformationId() == TRANSFORMATION_ID)
			{
				player.untransform();
			}
		}
		_players = null;
		
		// Announce event end
		Announcements.getInstance().announceToAll("ウサギ イベント: イベントは終わったよ。");
		
		return true;
	}
	
	@Override
	public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
	{
		String htmltext = null;
		switch (event)
		{
			//-------------------------------------------------------
			case "900101-1.htm":
			{
				return event;
			}
			case "transform":
			{
				if (player.isTransformed() || player.isInStance())
				{
					player.untransform();
				}
				
				getSkill(RABBIT_TRANSFORMATION).applyEffects(npc, player);
				_players.put(player.getObjectId(), player);
				break;
			}
			case "END_RABBITS_EVENT":
			{
				Announcements.getInstance().announceToAll("ウサギ イベント: 時間切れ！");
				eventStop();
				break;
			}
			//-------------------------------------------------------
			case "DONDON_TASK": // repeat
			{
				assert npc == _dondon || null == _dondon;
				for (int i = 0, index = getRandom(TOTAL_CHEST_COUNT); i < TOTAL_CHEST_COUNT; ++i, index = (index + 1) % TOTAL_CHEST_COUNT)
				{
					L2MonsterInstance chest = _chests[index];
					if (!canSeeMe(chest) && !chest.isDead() && !chest.isDecayed())
					{
						npc.teleToLocation(chest.getX(), chest.getY(), chest.getZ());
						startQuestTimer("DONDON_SAY", 3000, npc, null);
						break;/*for*/
					}
				}
				break;
			}
			case "DONDON_SAY":
			{
				assert npc == _dondon || null == _dondon;
				autoChat(npc, DONDON_SAY, Say2.ALL);
				break;
			}
			//-------------------------------------------------------
			case "SHOW":
			{
				assert npc.getId() == CHEST;
				L2MonsterInstance chest = (L2MonsterInstance) npc;
				if (canSeeMe(chest)) break;
				
				showMe(chest);
				chest.broadcastPacket(new AbstractNpcInfo.NpcInfo(chest, null));
				
				final boolean bonus = Rnd.get(100) <= BONUS_RATE;
				if (bonus)
				{
					Skill firework = CommonSkill.FIREWORK.getSkill();
					chest.broadcastPacket(new MagicSkillUse(chest, chest, firework.getId(), firework.getLevel(), firework.getHitTime(), firework.getReuseDelay()));
				}
				if (bonus) chest.getVariables().set("bonus", true); else chest.getVariables().remove("bonus");
				
				autoChat(chest, TRIGGERE_SAY, Say2.ALL);
				chest.getVariables().set("lastSocialBroadcast", System.currentTimeMillis());
				startQuestTimer("RANDAM_SAY", Rnd.get(5000, 7500), chest, null, true);
				startQuestTimer("HIDE_1", Rnd.get(15000, 20000), chest, null); // 15sec.
				break;
			}
			case "RANDAM_SAY": // repeat
			{
				assert npc.getId() == CHEST;
				L2MonsterInstance chest = (L2MonsterInstance) npc;
				
				QuestTimer t;
				if (!canSeeMe(chest) || chest.isDecayed() || chest.isDead())
				{
					cancelQuestTimer(event, npc, player);
				}
				else if ((t = getQuestTimer("HIDE_1", chest, null)) != null && t.getRemainDelay() < MINIMAL_SOCIAL_INTERVAL)
				{
					cancelQuestTimer(event, npc, player);
				}
				else
				{
					// Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
					long now = System.currentTimeMillis();
					if (now - chest.getVariables().getLong("lastSocialBroadcast", 0) > MINIMAL_SOCIAL_INTERVAL)
					{
						chest.getVariables().set("lastSocialBroadcast", now);
						autoChat(chest, RANDAM_SAY, Say2.ALL);
					}
				}
				break;
			}
			case "HIDE_1":
			{
				assert npc.getId() == CHEST;
				L2MonsterInstance chest = (L2MonsterInstance) npc;
				if (!canSeeMe(chest) || chest.isDead() || chest.isDecayed()) break;
				
				cancelQuestTimer("RANDAM_SAY", npc, null);
				autoChat(chest, HIDE_SAY, Say2.ALL);
				startQuestTimer("HIDE_2", 1000, chest, null);
				break;
			}
			case "HIDE_2":
			{
				assert npc.getId() == CHEST;
				L2MonsterInstance chest = (L2MonsterInstance) npc;
				if (!canSeeMe(chest) || chest.isDead() || chest.isDecayed()) break;
				
				chest.decayMe();
				hideMe(chest);
				chest.spawnMe();
				break;
			}
			//-------------------------------------------------------
			default:
			{
				_log.log(Level.SEVERE, "Rabbits: onAdvEvent(\"" + event + "," + (npc == null ? "NULL" : npc.getId() + " " + npc.getName()) + "," + (player == null ? "NULL" : player.getName()) + ")");
			}
		}
		return htmltext;
	}
	
	@Override
	public boolean onCanSeeMe(L2Npc npc, L2PcInstance player)
	{
		return canSeeMe(npc);
	}
	
	@Override
	public String onFirstTalk(L2Npc npc, L2PcInstance player)
	{
		switch (npc.getId())
		{
			case NPC_MANAGER:
				return "900101.htm";
			case CHEST:
				return "900102.htm";
			default:
				_log.log(Level.SEVERE, "Rabbits: onFirstTalk(" + npc.getId() + "'" + npc.getName() + "'>,<'" + player.getName() + "')");
		}
		return super.onFirstTalk(npc, player);
	}
	
	@Override
	public String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
	{
		final L2MonsterInstance chest = ((L2MonsterInstance) npc);
		if (skill.getId() == RABBIT_TORNADO_ID)
		{
			if (com.l2jserver.gameserver.util.Util.contains(targets, npc))
			{
				autoChat(npc, ON_DROP_SAY, Say2.ALL);	//+[JOJO]
				dropItem(npc, caster, DROPLIST, DROPLIST_TOTAL_CHANCE);
				if (chest.getVariables().getBoolean("bonus", false))
					dropItem(npc, caster, BONUSLIST, BONUSLIST_TOTAL_CHANCE);
				npc.doDie(npc);
				
				if (_chest_count.decrementAndGet() == 0)
				{
					Announcements.getInstance().announceToAll("ウサギ イベント: 宝箱は全部無くなったよ。");
					eventStop();
				}
			}
		}
		else if (skill.getId() == RABBIT_MAGIC_EYE_ID)
		{
			if (npc.isInsideRadius(caster, skill.getAffectRange(), false, false))
			{
				startQuestTimer("SHOW", 0, chest, null);
			}
		}
		return super.onSkillSee(npc, caster, skill, targets, isSummon);
	}
	
	@Override
	public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon, Skill skill)
	{
		if (_isActive && (skill == null || skill.getId() != RABBIT_TORNADO_ID))
		{
			getSkill(RAID_CURSE).applyEffects(npc, attacker);
		}
		return super.onAttack(npc, attacker, damage, isSummon);
	}
	
	private final void dropItem(L2Npc mob, L2PcInstance player, int[][] droplist, int total)
	{
		int chance = getRandom(total);
		
		for (int[] drop : droplist)
		{
			if ((chance -= drop[1]) < 0)
			{
				mob.dropItem(player, drop[0], getRandom(drop[2], drop[3]));
				return;
			}
		}
	}
	
	@Override
	public boolean eventBypass(L2PcInstance activeChar, String bypass)
	{
		return false;
	}
	
	private void autoChat(L2Npc npc, int[] stringId, int type)
	{
		NpcSay packet = new NpcSay(npc.getObjectId(), type, npc.getTemplate().getDisplayId(), stringId[Rnd.get(stringId.length)]);
		for (L2PcInstance player : npc.getKnownList().getKnownPlayers().values())
			if (player != null && player.getTransformationId() == TRANSFORMATION_ID)
				player.sendPacket(packet);
	}
	
	private final boolean canSeeMe(L2Npc npc)
	{
		return !npc.isInvisible();
	}
	
	protected final void hideMe(L2Npc npc)
	{
		assert !npc.isInvisible();
		npc.setInvisible(true);
	}
	
	protected final void showMe(L2Npc npc)
	{
		assert npc.isInvisible();
		npc.setInvisible(false);
	}
	
	public static void main(String[] args)
	{
		new Rabbits();
	}
}
