/*
 	OrpheusMS: MapleStory Private Server based on OdinMS
    Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net>
    				Patrick Huy <patrick.huy@frz.cc>
					Matthias Butz <matze@odinms.de>
					Jan Christian Meyer <vimes@odinms.de>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.server.handlers.channel;

import client.IItem;
import client.ISkill;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleCharacter.CancelCooldownAction;
import client.MapleClient;
import client.MapleInventory;
import client.MapleInventoryType;
import client.MapleWeaponType;
import client.SkillFactory;
import constants.ItemConstants;
import constants.ServerConstants;
import constants.skills.Aran;
import constants.skills.Buccaneer;
import constants.skills.NightLord;
import constants.skills.NightWalker;
import constants.skills.Shadower;
import constants.skills.ThunderBreaker;
import constants.skills.WindArcher;
import tools.Randomizer;
import net.MaplePacket;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import server.TimerManager;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;

public final class RangedAttackHandler extends AbstractDealDamageHandler {

	@Override
	public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
		MapleCharacter player = c.getPlayer();
		AttackInfo attack = parseDamage(slea, player, true);
		if (attack.skill == Buccaneer.ENERGY_ORB || attack.skill == ThunderBreaker.SPARK || attack.skill == Shadower.TAUNT || attack.skill == NightLord.TAUNT) {
			player.getMap().broadcastMessage(player, MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false);
			applyAttack(attack, player, 1);
		} else if (attack.skill == Aran.COMBO_SMASH || attack.skill == Aran.COMBO_PENRIL || attack.skill == Aran.COMBO_TEMPEST) {
			player.getMap().broadcastMessage(player, MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, 0, attack.allDamage, attack.speed, attack.direction, attack.display), false);
			if (attack.skill == Aran.COMBO_SMASH && player.getCombo() >= 30) {
				applyAttack(attack, player, 1);
			} else if (attack.skill == Aran.COMBO_PENRIL && player.getCombo() >= 100) {
				applyAttack(attack, player, 2);
			} else if (attack.skill == Aran.COMBO_TEMPEST && player.getCombo() >= 200) {
				applyAttack(attack, player, 4);
			}
		} else {
			IItem weapon = player.getInventory(MapleInventoryType.EQUIPPED).getItem((byte) -11);
			MapleWeaponType type = MapleItemInformationProvider.getInstance().getWeaponType(weapon.getItemId());
			if (type == MapleWeaponType.NOT_A_WEAPON) {
				return;
			}
			int projectile = 0;
			byte bulletCount = 1;
			MapleStatEffect effect = null;
			if (attack.skill != 0) {
				effect = attack.getAttackEffect(player, null);
				bulletCount = effect.getBulletCount();
				if (effect.getCooldown() > 0) {
					c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect.getCooldown()));
				}
			}
			boolean hasShadowPartner = player.getBuffedValue(MapleBuffStat.SHADOWPARTNER) != null;
			if (hasShadowPartner) {
				bulletCount *= 2;
			}
			MapleInventory inv = player.getInventory(MapleInventoryType.USE);
			for (byte i = 0; i < inv.getSlotLimit(); i++) {
				IItem item = inv.getItem(i);
				if (item != null) {
					int id = item.getItemId();
					boolean bow = ItemConstants.isArrowForBow(id);
					boolean cbow = ItemConstants.isArrowForCrossBow(id);
					if (item.getQuantity() > (bulletCount == 1 ? 0 : bulletCount)) {
						if (type == MapleWeaponType.CLAW && ItemConstants.isThrowingStar(id) && weapon.getItemId() != 1472063) {
							if (((id == 2070007 || id == 2070018) && player.getLevel() < 70) || (id == 2070016 && player.getLevel() < 50)) {
							} else {
								projectile = id;
								break;
							}
						} else if ((type == MapleWeaponType.GUN && ItemConstants.isBullet(id))) {
							if (id == 2331000 && id == 2332000) {
								if (player.getLevel() > 69) {
									projectile = id;
									break;
								}
							} else if (player.getLevel() > (id % 10) * 20 + 9) {
								projectile = id;
								break;
							}
						} else if ((type == MapleWeaponType.BOW && bow) || (type == MapleWeaponType.CROSSBOW && cbow) || (weapon.getItemId() == 1472063 && (bow || cbow))) {
							projectile = id;
							break;
						}
					}
				}
			}
			boolean soulArrow = player.getBuffedValue(MapleBuffStat.SOULARROW) != null;
			boolean shadowClaw = player.getBuffedValue(MapleBuffStat.SHADOW_CLAW) != null;
			if (!soulArrow && !shadowClaw && attack.skill != 11101004 && attack.skill != 15111007 && attack.skill != 14101006) {
				byte bulletConsume = bulletCount;
				if (effect != null && effect.getBulletConsume() != 0) {
					bulletConsume = (byte) (effect.getBulletConsume() * (hasShadowPartner ? 2 : 1));
				}
				if (!ServerConstants.UNLIMITED_PROJECTILES) {
					MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, projectile, bulletConsume, false, true);
				}
			}

			if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) {
				int visProjectile = projectile; // visible projectile sent to
												// players
				if (ItemConstants.isThrowingStar(projectile)) {
					MapleInventory cash = player.getInventory(MapleInventoryType.CASH);
					for (int i = 0; i < 96; i++) { // impose order...
						IItem item = cash.getItem((byte) i);
						if (item != null) {
							if (item.getItemId() / 1000 == 5021) {
								visProjectile = item.getItemId();
								break;
							}
						}
					}
				} else // bow, crossbow
				if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) {
					visProjectile = 0;
				}
				MaplePacket packet;
				switch (attack.skill) {
					case 3121004: // Hurricane
					case 3221001: // Pierce
					case 5221004: // Rapid Fire
					case 13111002: // KoC Hurricane
						packet = MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.rangedirection, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display);
						break;
					default:
						packet = MaplePacketCreator.rangedAttack(player, attack.skill, attack.skilllevel, attack.stance, attack.numAttackedAndDamage, visProjectile, attack.allDamage, attack.speed, attack.direction, attack.display);
						break;
				}
				player.getMap().broadcastMessage(player, packet, false, true);
				if (effect != null) {
					int money = effect.getMoneyCon();
					if (money != 0) {
						int moneyMod = money / 2;
						money += Randomizer.nextInt(moneyMod);
						if (money > player.getMeso()) {
							money = player.getMeso();
						}
						player.gainMeso(-money, false);
					}
				}
				if (attack.skill != 0) {
					ISkill skill = SkillFactory.getSkill(attack.skill);
					MapleStatEffect effect_ = skill.getEffect(player.getSkillLevel(skill));
					if (effect_.getCooldown() > 0) {
						if (player.skillisCooling(attack.skill)) {
							return;
						} else {
							c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect_.getCooldown()));
							player.addCooldown(attack.skill, System.currentTimeMillis(), effect_.getCooldown() * 1000, TimerManager.getInstance().schedule(new CancelCooldownAction(player, attack.skill), effect_.getCooldown() * 1000));
						}
					}
				}
				if ((player.getSkillLevel(SkillFactory.getSkill(NightWalker.VANISH)) > 0 || player.getSkillLevel(SkillFactory.getSkill(WindArcher.WIND_WALK)) > 0) && player.getBuffedValue(MapleBuffStat.DARKSIGHT) != null && attack.numAttacked > 0 && player.getBuffSource(MapleBuffStat.DARKSIGHT) != 9101004) {
					player.cancelEffectFromBuffStat(MapleBuffStat.DARKSIGHT);
					player.cancelBuffStats(MapleBuffStat.DARKSIGHT);
				}
				applyAttack(attack, player, bulletCount);
			}
		}
	}
}