package biz.plexers.sparrow.core;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import biz.plexers.sparrow.core.UpgradableShipAttribute.Choices;
import biz.plexers.sparrow.db.Arggg;
import biz.plexers.sparrow.db.DbHelper;
import biz.plexers.sparrow.db.DbManager;
import biz.plexers.sparrow.mp.Turn;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@JsonSerialize(using = Ship.Serializer.class)
public class Ship extends Arggg {

	private HashMap<UpgradableShipAttribute.Choices, UpgradableShipAttribute> upgradableShipAttributes;
	private HashMap<InBattleShipAttribute.Choices, InBattleShipAttribute> inBattleShipAttributes;
	private HashMap<StandardShipAttribute.Choices, StandardShipAttribute> standardShipAttributes;
	private double goldValue;
	private String name;

	public String getName() {
		return name;
	}

	public void applyUpgrade(Upgrade upgrade) throws Exception {
		if (!UserManager.getPirate().pay(upgrade)) {
			throw new Exception("Insufficient resources for this upgrade!");
		}
		for (UpgradableShipAttribute attribute : upgradableShipAttributes
				.values()) {
			Choices choice = attribute.getType();
			UpgradableShipAttribute otherAttribute = upgrade
					.getAttribute(choice);
			attribute.consume(otherAttribute);
		}

		DbManager.savePirate();

	}
	
	private int availableCrew(){
		UpgradableShipAttribute.Choices upgradableType = UpgradableShipAttribute.Choices.Crew;
		UpgradableShipAttribute crewOnBoard = upgradableShipAttributes.get(upgradableType);
		InBattleShipAttribute.Choices inbattleType = InBattleShipAttribute.Choices.WoundedCrew;
		InBattleShipAttribute woundedCrew = inBattleShipAttributes.get(inbattleType);
		return crewOnBoard.getValue() - woundedCrew.getValue();
	}

	public boolean hasEnoughCrewFor(Turn t) {
		int crewRequirements = t.getCrewRequirements();
		int availableCrew = this.availableCrew();
		return crewRequirements <= availableCrew;
	}

	public double getGoldValue() {
		return goldValue;
	}

	public void engageBattle() {
		for(InBattleShipAttribute a: inBattleShipAttributes.values()) {
			a.setValue(0);
		}
	}

	public double getAttributeValue(ShipAttribute<Choices> choice){
		ShipAttribute<InBattleShipAttribute.Choices> chosenAttribute = this.inBattleShipAttributes.get(choice);
		if (chosenAttribute != null) return chosenAttribute.getValue();
		return this.upgradableShipAttributes.get(choice).getValue();
	}
	
	public boolean changeAttributeBy(ShipAttribute<Choices> choice, double offset){
		ShipAttribute<InBattleShipAttribute.Choices> chosenAttribute = this.inBattleShipAttributes.get(choice);
		if (chosenAttribute != null){
			chosenAttribute.changeValueBy(offset);
			return true;
		}
		return false;
	}
	
	public double getPresentValue() {
		// TODO Improve Calculations
		return goldValue;
	}

	@SuppressWarnings("unchecked")
	private Ship(Map<String, Object> props) {
		goldValue = (double) props.get("goldValue");
		name = (String) props.get("name");
		Object upgradableMap = props.get("upgradableShipAttributes");
		upgradableShipAttributes = DbHelper
				.mapAsObject(
						upgradableMap,
						new TypeReference<HashMap<UpgradableShipAttribute.Choices, UpgradableShipAttribute>>() {
						});
		Object inBattleMap = props.get("inBattleShipAttributes");
		inBattleShipAttributes = DbHelper
				.mapAsObject(
						inBattleMap,
						new TypeReference<HashMap<InBattleShipAttribute.Choices, InBattleShipAttribute>>() {
						});
		standardShipAttributes = DbHelper
				.mapAsObject(
						props.get("standardShipAttributes"),
						new TypeReference<HashMap<StandardShipAttribute.Choices, StandardShipAttribute>>() {
						});
	}

	@JsonCreator
	public static Ship factory(Map<String, Object> props) {
		return new Ship(props);
	}

	public static class Serializer extends JsonSerializer<Ship> {

		@Override
		public void serialize(Ship value, JsonGenerator jgen,
				SerializerProvider provider) throws IOException,
				JsonProcessingException {
			jgen.writeStartObject();
			jgen.writeNumberField("goldValue", value.goldValue);
			jgen.writeStringField("name", value.name);
			jgen.writeObjectField("inBattleShipAttributes",
					value.inBattleShipAttributes);
			jgen.writeObjectField("upgradableShipAttributes",
					value.upgradableShipAttributes);
			jgen.writeObjectField("standardShipAttributes",
					value.standardShipAttributes);
			jgen.writeEndObject();

		}

	}

}

@@ -2512,32 +2512,34 @@
 0);%0D%0A%09%09%7D%0D%0A%09%7D%0D%0A%0D%0A
+%0D%0A
 %09public double g
@@ -2540,16 +2540,30 @@
 uble get
+UpgradableShip
 Attribut
@@ -2569,16 +2569,26 @@
 teValue(
+Upgradable
 ShipAttr
@@ -2588,33 +2588,32 @@
 hipAttribute
-%3C
+.
 Choices
-%3E
  choice)%7B%0D%0A%09
@@ -2617,178 +2617,157 @@
 %0D%0A%09%09
-ShipAttribute%3CInBattleShipAttribute.Choices%3E chosenAttribute = this.inBattleShipAttributes.get(choice);%0D%0A%09%09if (chosenAttribute != null) return chosenAttribute.getValue();
+return this.upgradableShipAttributes.get(choice).getValue();%0D%0A%09%7D%0D%0A%09public double getInBattleShipAttributeValue(InBattleShipAttribute.Choices choice)%7B
 %0D%0A%09%09
@@ -2774,32 +2774,30 @@
 return this.
-upgradab
+inBatt
 leShipAttrib
@@ -2825,32 +2825,35 @@
 alue();%0D%0A%09%7D%0D%0A%09%0D%0A
+%09%0D%0A
 %09public boolean 

Implement getAttributeValue as two separate methods