public abstract class Entity implements Spawnable, Serializable {

	private static final long serialVersionUID = -8544332465232461099L;
	protected transient final long identity;
	protected final String name;
	
	protected volatile int mesh;
	protected volatile int hairstyle;
	protected volatile Location location;
	protected volatile int HP;
	protected volatile int mana;
	protected volatile int level;
	protected volatile long flags = 0;
	public final transient View view;

	/**
	 * Construct a new entity
	 * @param identity
	 * @param mesh
	 * @param hairstyle
	 * @param name
	 * @param location
	 * @param HP
	 */
	public Entity(long identity, int mesh, int hairstyle, String name, Location location, int HP) {
		this.identity = identity;
		this.location = location;
		this.mesh = mesh;
		this.hairstyle = hairstyle;
		this.name = name;
		this.HP = HP;
		this.view = new ViewImpl(this);
	}
	
	/**
	 * Set the location for this entity
	 * @param location
	 */
	public void setLocation(Location location) {
		if(this.location != null && this.location.getMap() != location.getMap()) {
				this.location.getMap().removeEntity(this);
		}
		location.getMap().addEntity(this);
		this.location = location;
		this.view.update(location);
	}
	
	/**
	 * The walk method is called when an Entity moves.
	 * This can be called from AI (monsters/NPCs) or an Entity Move
	 * packet from the client. When existing, the packet can be
	 * forwarded. Otherwise, it needs to be created runtime.
	 * 
	 * When the Entity moves, old surroundings should receive
	 * an entity remove packet. New surroundings should receive
	 * an entity spawn packet. The intersection of surroundings
	 * should receive the delegated entity move packet.
	 * 
	 * @param direction
	 * @param walkPacket
	 */	
	public void walk(int direction, IncomingPacket walkPacket) {
		PacketWriter pw = walkPacket != null ? new PacketWriter(walkPacket) : 
			new PacketWriter(PacketType.ENTITY_MOVE_PACKET, 12)
				.putUnsignedInteger(identity)
				.putUnsignedByte(direction)
				.putUnsignedByte(1);
		// Send walk packet to me and surroundings
		for(Player player : view.getPlayers())
			pw.send(player);
		// Change location and send spawn / entity remove packets
		setLocation(this.location.inDirection(direction));
	}
	
	/**
	 * @param x
	 * @param y
	 * @param direction
	 * @param jumpPacket
	 */
	public void jump(int x, int y, int direction, IncomingPacket jumpPacket) {
//		PacketWriter pw = jumpPacket != null ? new PacketWriter(jumpPacket) : 
//			new GeneralData(GeneralData.SubType.JUMP, this).setDwParam(y << 16 | x).build();
	
		PacketWriter pw = new PacketBuilder(new GeneralDataPacket(SubType.JUMP, this)
				.setDwParam(y << 16 | x)).build();
		// Send jump packet to me and surroundings
		for(Player player : view.getPlayers())
			pw.send(player);
		// Change location and send spawn / entity remove packets
		setLocation(new Location(this.location.map, x, y, direction));
			
	}

	/**
	 * This method is called to setup the initial spawn. It adds the Entity
	 * to a new Map and sends the Spawn packet to the surrounding Players.
	 */
	public void spawn() {
		if(location != null)
			setLocation(location);
	}

	/**
	 * Remove the entity
	 */
	public void remove() {
		if(location != null) {
			location.getMap().removeEntity(this);
			PacketWriter remove = new GeneralData(SubType.ENTITY_REMOVE, this).build();
			for(Player player : view.getPlayers())
				if(player!=this)
					remove.send(player);
		}
	}

	/**
	 * Set a flag for this Entity
	 * @param flag
	 */
	public void setFlag(Flag flag) {
		flags |= flag.value;
	}

	/**
	 * Remove a flag for this Entity
	 * @param flag
	 */
	public void removeFlag(Flag flag) {
		flags ^= flag.value;
	}

	/**
	 * Check if this Entity has a given flag
	 * @param flag
	 * @return true if the Entity has the flag
	 */
	public boolean hasFlag(Flag flag) {
		return (flags & flag.value) == flag.value;
	}

	/**
	 * @return the Flags for this entity
	 */
	public long getFlags() {
		return flags;
	}

	public int getMesh() {
		return mesh;
	}

	public void setMesh(int mesh) {
		this.mesh = mesh;
	}
	
	public int getHairstyle() {
		return hairstyle;
	}
	
	public void setHairstyle(int hairstyle) {
		this.hairstyle = hairstyle;
	}

	public int getMana() {
		return mana;
	}

	public void setMana(int mana) {
		this.mana = mana;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public long getIdentity() {
		return identity;
	}
	
	public String getName() {
		return name;
	}
	
	public int getHP() {
		return HP;
	}
	
	public void setHP(int HP) {
		// Prevent health points below zero
		if ( HP < 0 )
			HP = 0;
		int maxHP = getMaxHP();
		if ( HP > maxHP )
			HP = maxHP;
		this.HP = HP;
	}
	
	public boolean isDead() {
		return HP == 0;
	}
	
	public void dealDamage(int damage) {
		setHP(HP - damage);
	}
	
	public abstract int getMaxHP();
	public abstract int getMaxMana();
	public abstract void notify(PacketWriter writer);

	@Override
	public Location getLocation() {
		return location;
	}

	@Override
	public boolean inView(Spawnable spawnable) {
		return location.inView(spawnable.getLocation());
	}

	@Override
	public String toString() {
		return "Entity [identity=" + identity + ", name=" + name + ", mesh="
				+ mesh + ", hairstyle=" + hairstyle + ", location=" + location
				+ ", HP=" + HP + ", mana=" + mana + ", level=" + level + "]";
	}

	/**
	 * Types for Flags
	 * 
	 * @author Jan-Willem Gmelig Meyling
	 * @author Thomas Gmelig Meyling
	 *
	 */
	public static enum Flag {
		CLEAR(0),
		
		/**
		 * Once you attack white/red name players, your name will turn blue and
		 * players or guards/patrols may attack you without punishment
		 */
		BLUENAME(1),
		
		/**
		 * Poison animation
		 */
		POISON(2),
		
		/**
		 * Exp bar
		 */
		EXP(2 << 3),
		
		/**
		 * Unknown for now
		 */
		TRADE_OR_DEAD(2 << 4),
		
		/**
		 * Team leader (star)
		 */
		TEAMLEADER(2 << 5),
		
		/**
		 * Star of accuracy beneath entity
		 */
		ACCURACY(2 << 6),
		
		/**
		 * Shield around entity
		 */
		SHIELD(2 << 7),
				
		
		/**
		 * With this Flag an Stigma appears beneath the Entity. The Stigma always is in the
		 * new fixed form (red). When you want to use another animation render for different
		 * spell levels, you can use a StringPacket instead:
		 * 
		 * <pre>
		 * {@code
		 *  String animation = "attackup35"; // See ini/3DEffect.ini
		 * 	new PacketWriter(PacketType.STRING_PACKET, 11 + animation.length())
		 * 	  .putUnsignedInteger(client.getPlayer().getIdentity())
		 * 	  .putUnsignedByte(10) // Type
		 * 	  .putUnsignedByte(1) // Str count
		 * 	  .putUnsignedByte(animation.length()) // Str length
		 * 	  .putString(animation)
		 * 	  .send(client);
		 * }
		 * </pre>
		 */
		STIGMA(2 << 8),
		
		REVIVE_COUNTER_GHOST(2 << 9),
		FADE_AWAY(2 << 10),
		REDNAME(2 << 13),
		BLACKNAME(2 << 14),
		REFLECT(2 << 16),
		SUPERMAN(2 << 17),
		BALL(2 << 18), BALL2(2 << 19),
		INVISIBLE(2 << 21),
		CYCLONE(2 << 22),
		DODGE(2 << 25),
		FLY(2 << 26),
		INTENSIFY(2 << 27),
		LUCKYTIME(2 << 29),
		PRAY(2 << 30),
		FATAL_STRIKE(0x800000000000l),
		CHAIN_LIGHTING(268435456l);
		
		public final long value;
		private Flag(long value) { this.value = value; }
	}

	
} public class Inventory {
		
	private static final int CAPACITY = 40;
	public static final int INVENTORY = 0;
	public static final int HELM = 1;
	public static final int NECKLACE = 2;
	public static final int ARMOR = 3;
	public static final int RIGHT_HAND = 4;
	public static final int LEFT_HAND = 5;
	public static final int RING = 6;
	public static final int TALISMAN = 7;
	public static final int BOOTS = 8;
	public static final int GARMENT = 9;
	// public static final int ATTACK_TALISMAN = 10;
	// public static final int DEFENSE_TALISMAN = 11;
	// public static final int STEED = 12;	
	private int position = 0;
	private final ItemInstance[] items = new ItemInstance[CAPACITY];
	private final EquipmentInstance[] equipments = new EquipmentInstance[10];
	/**
	 * Add an item to the Players inventory and update the client through
	 * an ItemInformation Packet
	 * @param item to be added to the inventory
	 * @return true if the item is successfully added to the inventory,
	 * false if the inventory was full
	 */
	public boolean addItem(ItemInstance item) {
		if(position < CAPACITY) {
			items[position++] = item;
			// Send a packet to the client to add the item to the inventory
			if(sent) item.new ItemInformationPacket(Mode.DEFAULT, INVENTORY).send(client);
			return true;
		}
		return false;
	}
		
	/**
	 * Remove an item from the Players inventory and update the client through
	 * an ItemUsage packet
	 * @param item to be removed from the inventory
	 * @return true if the item is successfully removed from the inventory,
	 * false if the inventory did not contain the item
	 */
	public boolean removeItem(ItemInstance item) {
		for ( int i = 0; i < position; i++ ) {
			if (items[i] == item ) {
				for ( int j = i + 1; j < position; j++ ) {
					items[j-1] = items[j];
				}
				position--;
				// Send a packet to the client to remove the item from the inventory
				if(sent) new ItemUsage(item.uniqueIdentifier, INVENTORY, ItemUsage.Mode.RemoveInventory).build().send(client);
				return true;
			}
		}
		return false;
	}
		
	/**
	 * Equip an item. If the inventory contains the item, remove it from the
	 * inventory first. The item which used the equipment slot before this
	 * equip interaction, is transfered to the inventory. 
	 * Send a ItemInformation packet such that the newly equipped item
	 * appears in the client
	 * @param slot
	 * @param equipment
	 * @return true if successfully equipped the item, or false if not.
	 */
	public boolean equip(int slot, EquipmentInstance equipment) {
		if ( slot > 0 && slot < 11 ) {
			if ( contains(equipment) ) {
				// Remove the item from the inventory
				removeItem(equipment);
			}
			unequip(slot);
			equipments[slot] = equipment;
			// Send a packet to the client to equip the item at the equipment slot
			if(sent) equipment.new ItemInformationPacket(Mode.DEFAULT, slot).send(client);
			return true;
		} else {
			// Item can't be equipped
			return false;
		}
	}
	
	/**
	 * Unequip an item, and transfer the item to the inventory.
	 * @param slot
	 * @return true if the inventory is not full
	 */
	public boolean unequip(int slot) {
		// Slot should be an equipment slot and inventory should not be full
		if ( slot > 0 && slot < 11 && !isFull()) {
			EquipmentInstance oldItem = equipments[slot];
			if ( oldItem != null ) {
				addItem(oldItem);
				equipments[slot] = null;
				// Send a packet to the client to remove the item from the equipment slot
				if(sent) new ItemUsage(oldItem.uniqueIdentifier, slot, ItemUsage.Mode.RemoveEquipment).build().send(client);
			}
			return true;
		}
		return false;
	}
	
	/**
	 * @param item
	 * @return true if the inventory contains the given item
	 */
	public boolean contains(ItemInstance item) {
		for ( int i = 0; i < position; i++ )
			if ( items[i] == item )
				return true;
		return false;
	}
		
	/**
	 * @return true if the inventory is empty
	 */
	public boolean isEmpty() {
		return position == 0;
	}
		
	/**
	 * @return true if the inventory is full
	 */
	public boolean isFull() {
		return position == CAPACITY;
	}
		
	/**
	 * @return an array of items in the inventory
	 */
	public ItemInstance[] getItems() {
		ItemInstance[] result = new ItemInstance[position];
		for ( int i = 0; i < position; i++ )
			result[i] = items[i];
		return result;
	}
		
	/**
	 * @return the equipments for this Player
	 */
	public EquipmentInstance[] getEquipments() {
		int amount = 0;
		for ( int i = 0; i < equipments.length; i++ )
			if ( equipments[i] != null )
				amount++;
		EquipmentInstance[] result = new EquipmentInstance[amount];
		for ( int i = 0, k = 0; i < equipments.length; i++ )
			if ( equipments[i] != null )
				result[k++] = equipments[i];
		return result;
	}
		
	/**
	 * @param slot
	 * @return the SID for the equipment in a given slot, or 0.
	 */
	public long getEquipmentSID(int slot) {
		if(equipments[slot] != null)
			return equipments[slot].itemPrototype.identifier;
		return 0;
	}
		
	private boolean sent = false;

	/**
	 * Send the equipments and inventory contents to the client
	 */
	public void send() {
		if(!sent){
			for ( int i = 0; i < position; i++ )
				items[i].new ItemInformationPacket(Mode.DEFAULT, INVENTORY).send(client);
			for ( int i = 1; i < equipments.length; i++ )
				if ( equipments[i] != null )
					equipments[i].new ItemInformationPacket(Mode.DEFAULT, i).send(client);
			sent = true;
		}
	}
		
} public static class EquipmentInstance extends ItemInstance {
		
	public final EquipmentPrototype equipmentPrototype;
	
	// These attributes are declared volatile
	// because their values are not final, and a
	// thread may change the value. The other threads
	// must always use the newest version of these
	// attributes and should not cache the value.
	public volatile int dura;
	public volatile Socket firstSocket = Socket.None;
	public volatile Socket secondSocket = Socket.None;
	public volatile int plus;
	public volatile int bless;
	public volatile int enchant;
		
	/**
	 * Create a new EquipmentInstance
	 * @param UID
	 * @param ip
	 */
	public EquipmentInstance(Long UID, EquipmentPrototype ip) {
		super(UID, ip);
		this.equipmentPrototype = ip;
	}
	
	/**
	 * @param dura the dura to set
	 * @return this EquipmentInstance - builder pattern
	 */
	public EquipmentInstance setDura(int dura) {
		this.dura = dura;
		return this;
	}
	
	/**
	 * @param firstSocket the firstSocket to set
	 * @return this EquipmentInstance - builder pattern
	 */
	public EquipmentInstance setFirstSocket(Socket firstSocket) {
		this.firstSocket = firstSocket;
		return this;
	}
	
	/**
	 * @param secondSocket the secondSocket to set
	 * @return this EquipmentInstance - builder pattern
	 */
	public EquipmentInstance setSecondSocket(Socket secondSocket) {
		this.secondSocket = secondSocket;
		return this;
	}
	
	/**
	 * @param plus the plus to set
	 * @return this EquipmentInstance - builder pattern
	 */
	public EquipmentInstance setPlus(int plus) {
		this.plus = plus;
		return this;
	}
	
	/**
	 * @param bless the bless to set
	 * @return this EquipmentInstance - builder pattern
	 */
	public EquipmentInstance setBless(int bless) {
		this.bless = bless;
		return this;
	}
	
	/**
	 * @param enchant the enchant to set
	 * @return this EquipmentInstance - builder pattern
	 */
	public EquipmentInstance setEnchant(int enchant) {
		this.enchant = enchant;
		return this;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "EquipmentInstance [equipmentPrototype="
				+ equipmentPrototype + ", dura=" + dura + ", firstSocket="
				+ firstSocket + ", secondSocket=" + secondSocket
				+ ", plus=" + plus + ", bless=" + bless + ", enchant="
				+ enchant + ", uniqueIdentifier=" + uniqueIdentifier
				+ ", itemPrototype=" + itemPrototype + "]";
	}

	public static enum Socket {
		None(0),
		Empty(4),
		NormalPhoenix(1),
		RefinedPhoenix(2),
		SuperPhoenix(3),
		NormalDragon(11),
		RefinedDragon(12),
		SuperDragon(13),
		NormalFury(21),
		RefinedFury(22),
		SuperFury(23),
		NormalRainbowGem(31),
		RefinedRainbowGem(32),
		SuperRainbowGem(33),
		NormalKylinGem(41),
		RefinedKylinGem(42),
		SuperKylinGem(43),
		NormalVioletGem(51),
		RefinedVioletGem(52),
		SuperVioletGem(53),
		NormalMoonGem(61),
		RefinedMoonGem(62),
		SuperMoonGem(63),
		NormalTortoiseGem(71),
		RefinedTortoiseGem(72),
		SuperTortoiseGem(73);
			
		final int value;
		Socket(int value) { this.value = value; }
			
		public static Socket valueOf(int value) {
			for ( Socket s : Socket.values() )
				if ( s.value == value )
					return s;
			return null;
		}
	}
		
} public class IncomingPacket {
	
	final ByteBuffer buffer;
	private final PacketType packetType;
	
	/**
	 * Construct a new {@code IncommingPacket} based on a {@code ByteBuffer}
	 * @param buffer
	 * @throws UnimplementedPacketTypeException
	 */
	public IncomingPacket(ByteBuffer buffer) throws UnimplementedPacketTypeException {
		this.buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
		this.packetType = PacketType.valueOf(this.readUnsignedShort(2));
	}

	/**
	 * Returns unsigned byte at specified offset (1 byte)
	 * @param offset
	 * @return {short}  as the range of ubyte is 0 to 2^8-1
	 */
	public short readUnsignedByte(int offset) {
		return ((short) (buffer.get(offset) & (short) 0xff));
	}
	
	/**
	 * @param position
	 * @return byte at position
	 */
	public byte getByte(int position) {
		return buffer.get(position);
	}
	
	/**
	 * Returns unsigned short at specified offset (2 bytes)
	 * @param offset
	 * @return {int} as the range of ushort is 0 to 2^16-1
	 */
	public int readUnsignedShort(int offset) {
		return (buffer.getShort(offset) & 0xffff);
	}
	
	/**
	 * Returns unsigned integer at specified offset (4 bytes)
	 * @param offset
	 * @return long as the range of uint is 0 to 2^32-1
	 */
	public long readUnsignedInt(int offset) {
		return ((long) buffer.getInt(offset) & 0xffffffffL);
	}

	/**
	 * Returns string at specified offset and of specified length
	 * @param offset
	 * @param length
	 * @return {String}
	 */
	public String readString(int offset, int length) {
        byte[] output = new byte[length];
        System.arraycopy(buffer.array(), offset, output, 0, length);
		/*
		 * We had to replace the null termination for Strings because they did
		 * not work well with database models: ERROR: invalid byte sequence for
		 * encoding "UTF8": 0x00 FIX
		 */
        return new String(output).replaceAll("[\u0000]", "");
	}
	
	/**
	 * @return decrypted password
	 */
	public String readPassword() {
		/*
		 * We had to replace the null termination for Strings because they did
		 * not work well with database models: ERROR: invalid byte sequence for
		 * encoding "UTF8": 0x00 FIX
		 */
		return Cryptographer.decryptPassword(this, 20).replaceAll("[\u0000]", "");
	}
	
	/**
	 * @return the length for this packet, based on the data
	 */
	public int getLength() {
		return buffer.limit();
	}
	
	/**
	 * @return the {@code PacketType} for this {@code IncommingPacket}
	 */
	public PacketType getPacketType() {
		return packetType;
	}
	
	@Override
	public String toString() {
		return packetType.toString() + " : " + bytesToHex(buffer.array()) + " (" + getLength() + ")";
	}
	
	private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
	
	private static String bytesToHex(byte[] bytes) {
	    char[] hexChars = new char[bytes.length * 2];
	    for ( int j = 0; j < bytes.length; j++ ) {
	        int v = bytes[j] & 0xFF;
	        hexChars[j * 2] = hexArray[v >>> 4];
	        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
	    }
	    return new String(hexChars);
	}
	
} public class InteractPacket implements PacketHandler {
	private IncomingPacket ip; 
	
	private final long timestamp;
	private final long identity;
	private final long target;
	private final int x;
	private final int y;
	private final Mode mode;
	private Skill skill;
	
	/**
	 * Construct a new {@code InteractPacket} based on a {@code IncomingPacket}
	 * @param ip
	 */
	public InteractPacket(IncomingPacket ip) {
		this.ip = ip ;
		
		this.timestamp = ip.readUnsignedInt(4);
		this.identity = ip.readUnsignedInt(8);
		this.mode = Mode.valueOf(ip.readUnsignedByte(20));
		
		int skillid = ip.readUnsignedShort(24);
		skillid ^= 0x915d;
		skillid ^= identity & 0xFFFF;
		skillid = (skillid << 0x3 | skillid >> 0xd ) & 0xFFFF;
		skillid -= 0xeb42;
		
		this.skill = Skill.valueOf(skillid);
		
		// something seems to go wrong with high skill ids e.g. bless = 9876, that makes me have to do this weird conversion.
		if(this.skill == null)
		{
			this.skill = Skill.valueOf(skillid + (1 << 16));
		}
		
		long x = ip.readUnsignedShort(16);
		x = x ^ ( identity & 0xFFFF ) ^ 0x2ed6;
		x = ((x << 1) | ((x & 0x8000) >> 15)) & 0xffff;
        x |= 0xffff0000;
        x -= 0xffff22ee;
        this.x = (int) x;
        
        long y = ip.readUnsignedShort(18);;
        y = y ^ (identity & 0xffff) ^ 0xb99b;
        y = ((y << 5) | ((y & 0xF800) >> 11)) & 0xffff;
        y |= 0xffff0000;
        y -= 0xffff8922;
        this.y = (int) y;

		this.target = ip.readUnsignedInt(12);
        /* 
         * TODO Some additional encoding seems to be required based on the C# sources.
         * However, in the packets we receive from the client (5013), the target is
         * sent normally. Something strange happens with the X and Y coordinates though.
         * 
         * target = (((target & 0xffffe000) >> 13) | ((target & 0x1fff) << 19));
         * target ^= 0x5F2D2463 ^ identity;
         * target =- 0x746F4AE6;
         * target =- 0x746F4AE6;
         */
        
        System.out.println(this.toString());
	}

	/**
	 * @return the timer value
	 */
	public long getTimer() {
		return timestamp;
	}

	/**
	 * @return the identity value
	 */
	public long getIdentity() {
		return identity;
	}

	/**
	 * @return the target value
	 */
	public long getTarget() {
		return target;
	}

	/**
	 * @return the X value
	 */
	public int getX() {
		return x;
	}

	/**
	 * @return the Y value
	 */
	public int getY() {
		return y;
	}

	/**
	 * @return the mode
	 */
	public Mode getMode() {
		return mode;
	}

	/**
	 * @return the damage
	 */
	public Skill getSkill() {
		return skill;
	}

	@Override
	public void handle(GameServerClient player) {
		switch(mode){
		case AcceptMarriage:
			break;
		case ArcherAttack:
		{
			PacketWriter outgoingPacket = new PhysicalAttack.ArcherAttack(player, this).build();
			if(outgoingPacket != null)
				outgoingPacket.sendTo(player.getPlayer().view.getPlayers());
			break;
		}
		case DashEffect:
			break;
		case Death:
			break;
		case MagicAttack:
			if(skill instanceof MagicSkill)
				((MagicSkill) skill).handle(player, this);
			break;
		case MagicReflect:
			break;
		case None:
			break;
		case PhysicalAttack:
		{
			PacketWriter outgoingPacket = new PhysicalAttack(player, this).build();
			if(outgoingPacket != null)
				outgoingPacket.sendTo(player.getPlayer().view.getPlayers());
			break;
		}
		case RequestMarriage:
			break;
		case RushAttack:
			break;
		case SendFlowers:
			break;
		case WeaponReflect:
			break;
		default:
			break;
		
		}
	}

	@Override
	public String toString() {
		return "InteractPacket [timestamp=" + timestamp + ", identity=" + identity
				+ ", target=" + target + ", x=" + x + ", y=" + y + ", mode="
				+ mode + ", skill=" + skill + "]";
	}
	
	/**
	 * An enumeration of Interaction modes
	 * @author Jan-Willem Gmelig Meyling
	 */
	public static enum Mode {
		None(0),
		PhysicalAttack(2),
		RequestMarriage(8),
		AcceptMarriage(9),
		SendFlowers(13),
		Death(14),
		RushAttack(20),
		MagicAttack(21),
		WeaponReflect(23),
		DashEffect(24),
		ArcherAttack(25),
		MagicReflect(26);
		
		public final int mode;
		
		private Mode(int mode) {
			this.mode = mode;
		}
		
		/**
		 * @param mode
		 * @return the Mode for a given value
		 */
		public static Mode valueOf(int mode) {
			for(Mode m : Mode.values())
				if(m.mode == mode)
					return m;
			return null;
		}
	}

	@Override
	public PacketWriter build() {
		// TODO Auto-generated method stub
		return null;
	}

} public  enum PacketType {
	
	/**
	 * This packet is sent to the Account Server during login. It contains the
	 * login information such as the Account Name, Password, and Server which
	 * the client wishes to connect to. The Account Server must check this
	 * information with the database.
	 */
	AUTH_LOGIN_PACKET	(0x041B),
	
	/**
	 * The Auth Message Packet (0x41C) is sent by the client during initial
	 * Authentication, it is sent to the game server, the game server uses the
	 * keys to determine which account to link to the client.
	 * 
	 * It is also sent by the auth server when you get your username/password
	 * wrong or the server is down for example, the packet contains the string
	 * of bytes which identify the message to display. (For example: B7 FE CE F1
	 * C6 F7 CE B4 C6 F4 B6 AF)
	 */
	AUTH_LOGIN_RESPONSE (0x041C),
	
	/**
	 * The Authorization Response (0x41F) is made by the Auth Server if the
	 * account is permitted to enter the Game Server.
	 */
	AUTH_LOGIN_FORWARD	(0x041F),
	
	/**
	 * This packet is used to send messages to other players and to instruct the
	 * client on what errors to display on login. The server uses this packet by
	 * routing it to it's destination (the other player).
	 */
	MESSAGE_PACKET		(0x03EC),

	/**
	 * The EntityMove packet, also known as the Walk packet, this packet is used
	 * by all entitys that can move, such as Players, Monsters and if you wish
	 * NPCs. The EntityMove packet, also known as the Walk packet, this packet
	 * is used by all entitys that can move, such as Players, Monsters and if
	 * you wish NPCs. The EntityMove packet, also known as the Walk packet, this
	 * packet is used by all entitys that can move, such as Players, Monsters
	 * and if you wish NPCs.
	 */
	ENTITY_MOVE_PACKET   (0x3ED),
	
	/**
	 * The Character Information packet, this packet is sent primarily during
	 * the login process to set the majority of your characters values.
	 */
	CHAR_INFO_PACKET	(0x03EE),
	
	/**
	 * Sent by the client in response to the NEW_ROLE string when the account
	 * being used to login doesn't have a character associated with it on the
	 * target server. On successfully handling the Character Creation Packet the
	 * server responds with a Chat Packet with the message "ANSWER_OK" sent to
	 * "ALLUSERS" with the type being ChatType.Dialog. The client will then
	 * disconnect and return to the login screen, or in later versions of the
	 * client proceed with the typical login procedure.
	 */
	CHARACTER_CREATION_PACKET(0x03E9),
	
	/**
	 * Send by the server to add and remove items to and from equipment slots,
	 * inventory, inspect- and trade windows
	 */
	ITEM_INFORMATION_PACKET(0x3F0),
	
	/**
	 * The General Data packet performs a variety of tasks for the client, these
	 * vary from moving the client around the game, to ending an XP skill.
	 */
	GENERAL_DATA_PACKET	(0x03F2),
	
	/**
	 * The Entity Spawn packet is used to spawn both Monsters and Characters, it
	 * does not spawn NPCs. The packet's layout changes slightly depending on
	 * which you are spawning.
	 */
	ENTITY_SPAWN_PACKET (0x03F6),
	
	STRING_PACKET(0x3F7),
	
	WEATHER_PACKET(0x3F8),
	
	/**
	 * The Entity Status packet, also known as the "Update packet" is used to
	 * change the appearance(in some cases), certain values unique to a
	 * character (such as level, stat points, exp) and show active abilities (in
	 * other cases). This packet can be used to send 1 status update, or many.
	 */
	UPDATE_PACKET(0x3F9),
	
	/**
	 * The Item Usage packet used for interacting with the warehouse and
	 * equiping/un-equiping/updating items.
	 */
	ITEM_USAGE_PACKET (0x3F1),
	
	/**
	 * The Interact packet is most commonly used for direct melee/archer
	 * attacks, but also used for certain player to player actions, such as
	 * marriage.
	 */
	INTERACT_PACKET(0x3FE),	
	
	/** for if a player loots more than 1k gold. */ 
	REWARD_PACKET(0x405),
	
	/**
	 * The Skill Packet is sent during login and when you learn a new skill.
	 */
	SKILL_PACKET(0x44F),
	
	/**
	 * The Skill Experience packet can be used to update both Skills and
	 * Proficiency.
	 */
	SKILL_UPDATE_PACKET(0x450),
	
	/**
	 * The Magic Attack Packet, used for offensive and defensive spells of all
	 * kind.
	 */
	SKILL_ANIMATION_PACKET(0x451),
	
	GUILD_INFORMATION(0x452),
	
	GUILD_REQUEST(0x453),
	
	GUILD_MEMBER_INFORMATION(0x458),
	
	/**
	 * The Weapon Proficiency Packet is sent during login and when you learn a
	 * new Proficiency.
	 */
	PROFICIENCY(0x401),
	
	
	MAP_STATUS(0x456),//,
	//PORTAL_PACKET (0x24);
	
	/** 
	 * Used to spawn an npc to the client.
	 */ 
	NPC_SPAWN_PACKET(0x7EE),
	
	/** 
	 * Used when Dialog NPCs are clicked.
	 */
	NPC_INITIAL_PACKET(0x7EF),
	
	/**
	 * Used to chat with npcs
	 */
	NPC_DIALOG_PACKET(0x7F0);
	
	private final int type;
	
	private PacketType(int type) {
		this.type = type;
	}
	
	public int getType() {
		return type;
	}
	
	/**
	 * @param id
	 * @return the {@code PacketType} for a specific ID
	 * @throws UnimplementedPacketTypeException when the
	 * {@code PacketType} could not be found.
	 */
	public static PacketType valueOf(int id) throws UnimplementedPacketTypeException {
		for ( PacketType pt : PacketType.values() ) {
			if ( pt.type == id )
				return pt;
		}
		throw new UnimplementedPacketTypeException(id);
	}
	
	public static class UnimplementedPacketTypeException extends RuntimeException {
		
		private static final long serialVersionUID = 1L;

		UnimplementedPacketTypeException(int type) {
			super("Unimplemented PacketType: " + type);
		}
		
	}
} public interface PacketWrapper {

	/*
	 * Todo add methods here
	 */
	
	/**
	 * 
	 * @return A PacketWriter based on this PacketWrapper
	 */
	PacketWriter build();
	
} public class PacketWriter {

	public final ByteBuffer buffer;
	private final PacketType packetType;
	
	/**
	 * Create a new PacketWriter
	 * @param packet
	 */
	public PacketWriter(IncomingPacket packet) {
		this.packetType = packet.getPacketType();
		this.buffer = packet.buffer;
		this.buffer.position(4);
	}

	/**
	 * Create a new PacketWriter
	 * @param packetType
	 * @param size
	 */
	public PacketWriter(PacketType packetType, int size) {
		this.packetType = packetType;
		this.buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
		this.putUnsignedShort(size);
		this.putUnsignedShort(this.packetType.getType());
	}
	
	/**
	 * Set the offset for this PacketWriter
	 * @param offset
	 * @return this PacketWriter
	 */
	public PacketWriter setOffset(int offset) {
		this.buffer.position(offset);
		return this;
	}
	
	/**
	 * Increment the offset for this PacketWriter
	 * @param amount
	 * @return this PacketWriter
	 */
	public PacketWriter incrementOffset(int amount) {
		return this.setOffset(this.buffer.position() + amount);
	}

	/**
	 * Method used to put a bool into the packet.
	 * 
	 * @param b
	 *            boolean value to put.
	 * @return this PacketWriter
	 */
	public PacketWriter putBoolean(boolean b) {
		return this.putUnsignedByte(b ? 1 : 0);
	}

	/**
	 * Pushes an unsigned byte to the packet Since unsigned bytes can grow up to
	 * 2^8-1, and Java bytes can only grow up to 2^7-1 We supply the byte as
	 * short.
	 * 
	 * @param ubyte
	 * @return this PacketWriter
	 */
	public PacketWriter putUnsignedByte(short ubyte) {
		buffer.put((byte) (ubyte & 0xff));
		return this;
	}

	/**
	 * Pushes an unsigned byte to the packet Since unsigned bytes can grow up to
	 * 2^8-1, and Java bytes can only grow up to 2^7-1 We supply the byte as
	 * short.
	 * 
	 * @param ubyte
	 * @return this PacketWriter
	 */
	public PacketWriter putUnsignedByte(int ubyte) {
		buffer.put((byte) (ubyte & 0xff));
		return this;
	}

	/**
	 * Pushes an unsigned short to the packet Since unsigned shorts can grow up
	 * to 2^16-1, and Java shorts can only grow up to 2^15-1 we supply the short
	 * as integer
	 * 
	 * @param ushort value to be put
	 * @return this PacketWriter
	 */
	public PacketWriter putUnsignedShort(int ushort) {
		buffer.putShort((short) (ushort & 0xffff));
		return this;
	}

	/**
	 * Pushes an unsigned integer to the packet Since unsigned integers can grow
	 * up to 2^32-1, and Java integers can only grow up to 2^31-1 we supply the
	 * integer as long
	 * 
	 * @param uint value to be put
	 * @return this PacketWriter
	 */
	public PacketWriter putUnsignedInteger(long uint) {
		buffer.putInt((int) (uint & 0xffffffffL));
		return this;
	}
	
	public PacketWriter putSignedInteger(int sint) {
		buffer.putInt(sint);
		return this;
	}
	
	/**
	 * Pushes an unsigned long to the packet. Since unsigned longs can grow
	 * up to 2^64-1, and Java integers can only grow up to 2^64-1 we supply the
	 * long as BigInteger
	 * 
	 * @param ulong value to be put
	 * @return this PacketWriter
	 */
	public PacketWriter putUnsignedLong(BigInteger ulong) {
		buffer.putLong(ulong.longValue());
		return this;
	}

	/**
	 * Method to put a Java String to a byte[] and put it to the packet
	 * 
	 * @param str value to put
	 * @return this PacketWriter
	 */
	public PacketWriter putString(String str) {
		buffer.put(str.getBytes());
		return this;
	}

	/**
	 * Method to put a Java String to a byte[] and put it to the packet
	 * 
	 * @param str value to put
	 * @param length to extend
	 * @return this PacketWriter
	 */
	public PacketWriter putString(String str, int length) {
		if(str.length() <= length) {
			this.putString(str);
			if(str.length() < length) {
				this.incrementOffset(length - str.length());
			}
		}
		return this;
	}

	/**
	 * @return the length for this packet
	 */
	public int getLength() {
		return buffer.capacity();
	}
	
	/**
	 * Send the packet in this PacketWriter to a Player
	 * @param player
	 */
	public void send(Player player) {
		send(player.getClient());
	}
	
	/**
	 * Send the packet in this PacketWriter to a List of Players
	 * @param players
	 */
	public void sendTo(List<Player> players) {
		for ( Player player : players )
			send(player);
	}
	
	/**
	 * Send the packet in this PacketWriter to an array of Players
	 * @param players
	 */
	public void sendTo(Player[] players) {
		for ( Player player : players )
			send(player);
	}

	/**
	 * Send the packet in this PacketWriter to a Client
	 * @param client
	 */
	public void send(AbstractClient client) {
		client.write(buffer);
	}

} public class GameServerClient extends AbstractClient {
	
	private final GameServer gameServer;
	private final Model model;

	public GameServerClient(GameServer gameServer, AsynchronousSocketChannel channel) {
		super(channel);
		this.gameServer = gameServer;
		this.model = gameServer.getModel();
	}

	@Override
	protected void handle(IncomingPacket incomingPacket) throws AccessException,
			IOException {
		Player player = this.getPlayer();
				
		switch(incomingPacket.getPacketType()) {
		case AUTH_LOGIN_RESPONSE:
			// Read the identity and token from the packet
			// and set these as keys for the cipher
			long identity = incomingPacket.readUnsignedInt(4);
			long token = incomingPacket.readUnsignedInt(8);
			this.setIdentity(identity);
			AuthorizationPromise promise = model.getAuthorizationPromise(identity);
			this.setKeys(token, identity);
			// Inform the client that the login was successful
			if (promise.hasCharacter())
			{	
				// Create the Entity object for the player, bound to
				// the current identity and client thread
				player = model.loadPlayer(promise);
				this.setPlayer(player);
                model.getGameServerTicks().addEntity(player);
				player.setClient(this);

                new PacketSerializer(new MessagePacket(MessagePacket.SYSTEM, MessagePacket.ALL_USERS, "ANSWER_OK")
                        .setMessageType(MessageType.LOGININFO)).serialize().send(this);
				// Send the character information packet
				player.characterInformation().send(this);
			}
			else
			{
                new PacketSerializer(new MessagePacket(MessagePacket.SYSTEM, MessagePacket.ALL_USERS, "NEW_ROLE")
                        .setMessageType(MessageType.LOGININFO)).serialize().send(this);
			}
			break;
		case ENTITY_MOVE_PACKET:
			player.walk(incomingPacket.readUnsignedByte(8), incomingPacket);
            model.getGameServerTicks().didInteract(player);
			break;
		case ITEM_USAGE_PACKET:
			new ItemUsage(incomingPacket).handle(this);
			break;
		/*case MESSAGE_PACKET:
			MessagePacket mp = new MessagePacket(incomingPacket);
			
			if(mp.getMessage().startsWith("/")) {
				new Command(mp).handle(this);
			} else {
				mp.handle(this);
			}
			break; */
		case CHARACTER_CREATION_PACKET:
			if (model.createCharacter(new Character_Creation_Packet(incomingPacket)))
			{
                new PacketSerializer(
                    new MessagePacket(MessagePacket.SYSTEM, MessagePacket.ALL_USERS, "ANSWER_OK")
                    .setMessageType(MessageType.LOGININFO)).serialize().send(this);
				this.close();
			}
			else
			{
                new PacketSerializer(
                    new MessagePacket(MessagePacket.SYSTEM, MessagePacket.ALL_USERS, "Failed to create character. Character name already in use.")
                    .setMessageType(MessageType.DIALOG))
                    .serialize().send(this);
			}
			break;
		case INTERACT_PACKET:
			new InteractPacket(incomingPacket).handle(this);
			break;
		case NPC_DIALOG_PACKET:
//			NPC_Dialog packet = player.getActiveDialog();
//			if(packet != null)
//			{
//				int input = incomingPacket.readUnsignedByte(10);
//				packet.setInput(input); 
//				packet.handle(this);
//			}
			break;
		case GUILD_MEMBER_INFORMATION:
			//new Guild_Member_Information_Packet(incomingPacket).handle(this, null);
			break;
		case GUILD_REQUEST:
			new Guild_Request_Packet(incomingPacket).handle(this);
			break;	
		case STRING_PACKET:
			new String_Packet(incomingPacket).handle(this);
			break;
		default:
            try {
//				PacketDeserializer pd = PacketDeserializerFactory.valueOf(incomingPacket.getPacketType())
//					.getInstance(incomingPacket);
//				pd.getHandlerStrategy(pd.deserialize()).handle(this);
                System.out.println("No old fashioned way of handling the packet, will use the "
                + "PacketDeserializer and PacketHandler");
                PacketDeserializer pd = new PacketDeserializer(incomingPacket);
                Packets.getHandlerStrategy(pd.deserialize()).handle(this);
            } catch (DeserializationException e) {
                e.printStackTrace();
            }
			break;
		}
	}

	@Override
	protected void connected() {
		gameServer.connect(this);
	}

	@Override
	protected void disconnected() {
		gameServer.disconnect(this);
	}

	/**
	 * @return the Model for this {@code Client}
	 */
	public Model getModel() {
		return model;
	}

	/**
	 * @return the GameServer for this {@code Client}
	 */
	public GameServer getGameServer() {
		return gameServer;
	}

} public static abstract class PassiveSkill extends Skill {
		
	protected Entity target; 
		
	public void setTarget(Entity target)
	{
		this.target = target;
	}
		
	/**
	 * @param level
	 * @return the chance for this skill to occur
	 */
	public abstract double chance(int level);
		
	/**
	 * @param level
	 * @return get the damage multiplier for this Skill
	 */
	public abstract double damageMutiplier(int level);
		
	/**
	 * @param level
	 * @return the range for this skill
	 */
	public abstract int range(int level);
		
	/**
	 * @param level
	 * @return the distance for this skill
	 */
	public abstract int distance(int level);
		
	/**
	 * @return the WeaponType required for this Skill
	 */
	public abstract WeaponType getWeaponType();
		
	/**
	 * @return the entities taking damage from the hit. 
	 */
	public abstract TargetBuilder getHittedEntities(GameServerClient client, int level);
}
import net.co.java.entity.Entity;
import net.co.java.entity.Player.Inventory;
import net.co.java.item.ItemInstance.EquipmentInstance;
import net.co.java.packets.IncomingPacket;
import net.co.java.packets.InteractPacket;
import net.co.java.packets.PacketType;
import net.co.java.packets.PacketWrapper;
import net.co.java.packets.PacketWriter;
import net.co.java.server.GameServerClient;
import net.co.java.skill.Skill.PassiveSkill;
package net.co.java.skill;


public class PhysicalAttack implements PacketWrapper {
	
	private GameServerClient client;  
	private Entity target; 
	private InteractPacket interactPacket;
	
	
	private final static int MELEE_RANGE = 2; // for now
	private final static int RANGED_RANGE = 13; // for now
	
	// Retreives the Entity object. 
	public PhysicalAttack(GameServerClient player, InteractPacket interactPacket) {
		this.client = player; 
		this.interactPacket = interactPacket;
		for(Entity e : client.getPlayer().getLocation().getMap().getEntities())
			if(e.getIdentity() == interactPacket.getTarget())
				this.target = e; 
	}

	// Computes if target is in range etc
	// TODO CHECK FOR TIME 
	public boolean validateHit(){
		TargetBuilder tb = new TargetBuilder(client.getPlayer()).inCircle(getRange());
		
		if(target != null)
		{
			for(Entity e : tb.getEntities())
				if(e == target)
					return true;
		}
		
		return false;
	}
	
	public boolean computeMiss(){
		return (Math.random() < 0.90); // 90 pc hit chance for now
	}
	
	public boolean doPassive(){
		//client.getPlayer().inventory.getEquipments()
		
		WeaponType wt1 = WeaponType.valueOf((int) (client.getPlayer().inventory.getEquipmentSID(Inventory.RIGHT_HAND) / 1000));
		WeaponType wt2 = WeaponType.valueOf((int) (client.getPlayer().inventory.getEquipmentSID(Inventory.LEFT_HAND) / 1000)); 
		
		PassiveSkill skill1 = Skill.passiveSkillOf(wt1);
		PassiveSkill skill2 = Skill.passiveSkillOf(wt2);
				
		// we shouldn't give the player twice as much chance.
		if(skill1 == skill2 && skill1 != null)
		{
			if(client.getPlayer().getSkills().containsKey(skill1))
			{
				if(Math.random() < skill1.chance(9))
				{
					passiveSkillHandle(skill1); 
					return true;
				}
			}
		} else if (skill1 != null && skill2 != null){
			if(client.getPlayer().getSkills().containsKey(skill1))
			{
				if(Math.random() < skill1.chance(9))
				{
					passiveSkillHandle(skill1); 
					return true;
				}
			}
			if(client.getPlayer().getSkills().containsKey(skill2))
			{
				if(Math.random() < skill2.chance(9))
				{
					passiveSkillHandle(skill2); 
					return true;
				}
			}
		} else if (skill1 != null) {
			if(client.getPlayer().getSkills().containsKey(skill1))
			{
				if(Math.random() < skill1.chance(9))
				{
					passiveSkillHandle(skill1); 
					return true;
				}
			}
		} else if (skill2 != null) {
			if(client.getPlayer().getSkills().containsKey(skill2))
			{
				if(Math.random() < skill2.chance(9))
				{
					passiveSkillHandle(skill2); 
					return true;
				}
			}
		}
		
		
		return false;
	}
	
	public long computeDamage(){
		return 4000;
	}
	
	public int getRange(){
		return MELEE_RANGE;
	}
	
	
	@Override
	public PacketWriter build() {
		if(validateHit())
		{
			if(doPassive()) {
				return null;				
			} else { 
				if(computeMiss()) {
					long damage = computeDamage();
					target.setHP((int) (target.getHP()-damage));
					target.spawn();
					
					return new PacketWriter(PacketType.INTERACT_PACKET, 28)