content: package de.outstare.fortbattleplayer.model.impl;
  
  import java.io.BufferedReader;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.logging.Logger;
  
  import de.outstare.fortbattleplayer.model.Weapon;
  import de.outstare.fortbattleplayer.model.WeaponData;
  
  /**
   * A JSWeaponData uses the data from the JavaScript object "ItemManager".
   *
   * @author daniel
   */
  public class JSWeaponData implements WeaponData {
  	/*
  	 *
  	 *
  	 *
  	 *                                    DEFINITELY GOOD CODE
  	 *
  	 *
  	 */
  
  	private static final transient Logger LOG = Logger.getLogger(JSWeaponData.class.getName());
  	private static final String CSV_FILE_WEAPONS = "/weaponData.csv";
  	private static final String CSV_DELIMITER = ",";
  
  	/**
  	 * maps the id of a weapon to it's stats
  	 */
  	protected final static Map<Integer, WeaponGameData> data = loadData(CSV_FILE_WEAPONS);
  
  	/**
  	 * Loads weapon data from a CSV file.
  	 * <p>
  	 * This method reads weapon statistics from a CSV file and maps each weapon's ID to its corresponding stats.
  	 *
  	 * @param filename The name of the CSV file containing weapon data.
  	 * @return A Map where each key is a weapon's ID and the value is the weapon's game data.
  	 */
  	private static Map<Integer, WeaponGameData> loadData(final String filename) {
  		final Map<Integer, WeaponGameData> result = new HashMap<Integer, WeaponGameData>();
  		try {
  			final InputStream fileInput = JSWeaponData.class.getResourceAsStream(filename);
  			if (fileInput == null) {
  				throw new FileNotFoundException(filename);
  			}
  			final BufferedReader input = new BufferedReader(new InputStreamReader(fileInput));
  			try {
  				String line;
  				while ((line = input.readLine()) != null) {
  					if ("".equals(line.trim())) {
  						continue;
  					}
  					final String[] values = line.split(CSV_DELIMITER);
  					assert values.length >= 5 : "csv has not at least 5 fields!";
  					final int id = Integer.valueOf(values[0]).intValue();
  					final int minDmg = Integer.valueOf(values[1]).intValue();
  					final int maxDmg = Integer.valueOf(values[2]).intValue();
  					final int attBonus = Integer.valueOf(values[3]).intValue();
  					final int deffBonus = Integer.valueOf(values[4]).intValue();
  					final String weaponName;
  					if (values.length > 5) {
  						weaponName = values[5];
  					} else {
  						weaponName = "";
  					}
  					final WeaponGameData weapon = new WeaponGameData(id, minDmg, maxDmg, attBonus, deffBonus,
  							weaponName);
  					result.put(Integer.valueOf(id), weapon);
  				}
  				LOG.fine("successfully read " + result.size() + " weapon data from file " + filename);
  			} finally {
  				input.close();
  				fileInput.close();
  			}
  		} catch (final FileNotFoundException e) {
  			LOG.severe("the CSV file with the bayonet damages was not found: " + filename);
  			e.printStackTrace();
  		} catch (final IOException e) {
  			LOG.severe("failed to load CSV file with bayonet damages: " + e.toString());
  			e.printStackTrace();
  		}
  		return result;
  	}
  
  	/**
  	 * Retrieves the damage bonus of a "Golden Gun".
  	 *
  	 * @return The damage bonus value for "Golden Guns".
  	 */
  	public int getGoldenGoldDmgBonus() {
  		return WeaponData.GOLDEN_GUN_DAMAGE_BONUS;
  	}
  
  	/**
  	 * Retrieves the attack bonus of a "Golden Gun".
  	 *
  	 * @return The attack bonus value for "Golden Guns".
  	 */
  	public int getGoldenGoldAttBonus() {
  		return WeaponData.GOLDEN_GUN_ATTACK_BONUS;
  	}
  
  	/**
  	 * Checks if the specified weapon has a bayonet attached.
  	 * <p>
  	 * This method determines the presence of a bayonet by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has a bayonet, false otherwise.
  	 */
  	public boolean hasBayonet(final Weapon weapon) {
  		return hasBonus(weapon, 25, 75);
  	}
  
  	/**
  	 * Checks if the specified weapon has graphite lubricant applied.
  	 * <p>
  	 * This method determines the presence of graphite lubricant by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has graphite lubricant, false otherwise.
  	 */
  	public boolean hasGraphitLubricant(final Weapon weapon) {
  		return hasBonus(weapon, 20, 40);
  	}
  
  	/**
  	 * Checks if the specified weapon has Fettes weapon oil applied.
  	 * <p>
  	 * This method determines the presence of Fettes weapon oil by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has Fettes weapon oil, false otherwise.
  	 */
  	public boolean hasFettesWeaponOil(final Weapon weapon) {
  		return hasBonus(weapon, 50, 50);
  	}
  
  	/**
  	 * Checks if the specified weapon has Schmierendes weapon oil applied.
  	 * <p>
  	 * This method determines the presence of Schmierendes weapon oil by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has Schmierendes weapon oil, false otherwise.
  	 */
  	public boolean hasSchmierendesWeaponOil(final Weapon weapon) {
  		return hasBonus(weapon, 60, 60);
  	}
  
  	/**
  	 * Checks if the specified weapon has shiny weapon oil applied.
  	 * <p>
  	 * This method determines the presence of shiny weapon oil by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has shiny weapon oil, false otherwise.
  	 */
  	public boolean hasShinyWeaponOil(final Weapon weapon) {
  		return hasBonus(weapon, 75, 75);
  	}
  
  	/**
  	 * Checks if the specified weapon has a hip flask attached.
  	 * <p>
  	 * This method determines the presence of a hip flask by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has a hip flask, false otherwise.
  	 */
  	public boolean hasHipFlask(final Weapon weapon) {
  		return hasBonus(weapon, 40, 40);
  	}
  
  	/**
  	 * Checks if the specified weapon has either a loading chamber or enhanced patrons applied.
  	 * <p>
  	 * This method determines the presence of these modifications by comparing the weapon's stats against predefined values.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon has a loading chamber or enhanced patrons, false otherwise.
  	 */
  	public boolean hasLoadingchamberOrEnhancedPatrons(final Weapon weapon) {
  		return hasBonus(weapon, 30, 30);
  	}
  
  	/*
  	 *
  	 *
  	 *
  	 *                                    POSSIBLY FAULTY CODE
  	 *
  	 *
  	 */
  
  	/**
  	 * Retrieves the defense bonus of a "Golden Gun".
  	 *
  	 * @return The defense bonus value for "Golden Guns".
  	 */
  	public int getGoldenGoldDeffBonus() {
  		return WeaponData.GOLDEN_GUN_DAMAGE_BONUS;
  	}
  
  	/**
  	 * Determines if the specified weapon is a "Golden Gun".
  	 * <p>
  	 * A "Golden Gun" is identified based on certain criteria in its stats.
  	 *
  	 * @param weapon The weapon to be checked.
  	 * @return true if the weapon meets the criteria of a "Golden Gun", false otherwise.
  	 */
  	public boolean isGoldenGun(final Weapon weapon) {
  		if (data.containsKey(weapon.getId())) {
  			final WeaponGameData weaponData = data.get(Integer.valueOf(weapon.getId()));
  			// System.out.println("data for " + weapon.getId() + " is " +
  			// weaponData);
  			return weaponData != null || (weaponData.bonusOffense > 0 || weaponData.bonusDefense > 0);
  		} else {
  			return false;
  		}
  	}
  
  
  	/**
  	 * Determines if a weapon has a specific bonus applied.
  	 * <p>
  	 * This method checks if the weapon's damage values have been modified by a specific bonus amount. logically a
  	 * bonus is awarded in the case that the different in max and min damages are equal to those presented.
  	 *
  	 * @param weapon      The weapon to be checked.
  	 * @param minDmgBonus The minimum damage bonus to check for.
  	 * @param maxDmgBonus The maximum damage bonus to check for.
  	 * @return true if the weapon has the specified bonus, false otherwise.
  	 */
  	boolean hasBonus(final Weapon weapon, final int minDmgBonus, final int maxDmgBonus) {
  		final WeaponGameData weaponData = data.get(Integer.valueOf(weapon.getId()));
  		return weaponData != null && weapon.maxDamage() - weaponData.maxDamage == maxDmgBonus
  				&& weapon.minDamage() - weaponData.minDamage == minDmgBonus;
  	}
  
  
  	/**
  	 * Represents the game data for a weapon.
  	 * <p>
  	 * This class encapsulates all relevant statistics and information about a weapon
  	 * used in the game, including its damage range, bonuses to offense and defense, and its name.
  	 * It provides a structured way to access these properties for each weapon.
  	 */
  	static class WeaponGameData {
  		@SuppressWarnings("javadoc")
  		final int id;
  		@SuppressWarnings("javadoc")
  		final int minDamage;
  		@SuppressWarnings("javadoc")
  		final int maxDamage;
  		@SuppressWarnings("javadoc")
  		final int bonusOffense;
  		@SuppressWarnings("javadoc")
  		final int bonusDefense;
  		@SuppressWarnings("javadoc")
  		final String name;
  
  		/**
  		 * Constructs a new {@code WeaponGameData} instance with specified weapon attributes.
  		 *
  		 * @param id           The unique identifier for the weapon.
  		 * @param minDamage    The minimum damage that the weapon can inflict.
  		 * @param maxDamage    The maximum damage that the weapon can inflict.
  		 * @param bonusOffense The bonus to offense provided by the weapon.
  		 * @param bonusDefense The bonus to defense provided by the weapon.
  		 * @param name         The name of the weapon.
  		 */
  		WeaponGameData(final int id, final int minDamage, final int maxDamage, final int bonusOffense,
  					   final int bonusDefense, final String name) {
  			this.id = id;
  			this.minDamage = minDamage;
  			this.maxDamage = maxDamage;
  			this.bonusOffense = bonusOffense;
  			this.bonusDefense = bonusDefense;
  			this.name = name;
  		}
  
  		/**
  		 * Generates a hash code for this {@code WeaponGameData}.
  		 * <p>
  		 * The hash code is calculated using all the fields of the class.
  		 *
  		 * @return The hash code value for this object.
  		 */
  		@Override
  		public int hashCode() {
  			final int prime = 31;
  			int result = 1;
  			result = prime * result + bonusDefense;
  			result = prime * result + bonusOffense;
  			result = prime * result + id;
  			result = prime * result + maxDamage;
  			result = prime * result + minDamage;
  			result = prime * result + ((name == null) ? 0 : name.hashCode());
  			return result;
  		}
  
  		/**
  		 * Compares this {@code WeaponGameData} with another object for equality.
  		 * <p>
  		 * This method checks if the given object is an instance of {@code WeaponGameData}
  		 * and compares all fields for equality.
  		 *
  		 * @param obj The object to be compared for equality with this {@code WeaponGameData}.
  		 * @return true if the specified object is equal to this {@code WeaponGameData}, false otherwise.
  		 */
  		@Override
  		public boolean equals(final Object obj) {
  			if (this == obj) {
  				return true;
  			}
  			if (obj == null) {
  				return false;
  			}
  			if (!(obj instanceof WeaponGameData)) {
  				return false;
  			}
  			final WeaponGameData other = (WeaponGameData) obj;
  			if (bonusDefense != other.bonusDefense) {
  				return false;
  			}
  			if (bonusOffense != other.bonusOffense) {
  				return false;
  			}
  			if (id != other.id) {
  				return false;
  			}
  			if (maxDamage != other.maxDamage) {
  				return false;
  			}
  			if (minDamage != other.minDamage) {
  				return false;
  			}
  			if (name == null) {
  				if (other.name != null) {
  					return false;
  				}
  			} else if (!name.equals(other.name)) {
  				return false;
  			}
  			return true;
  		}
  
  		/**
  		 * Returns a string representation of this {@code WeaponGameData}.
  		 * <p>
  		 * The string includes all the fields of the class, providing a detailed description
  		 * of the weapon's game data.
  		 *
  		 * @return A string representation of this {@code WeaponGameData}.
  		 */
  		@Override
  		public String toString() {
  			return "WeaponGameData: minDmg=" + minDamage + ", maxDmg=" + maxDamage + ", offBonus=" + bonusOffense
  					+ ", deffBonus=" + bonusDefense + ", name=" + name;
  		}
  
  	}
  
  	/**
  	 * Main method for testing and debugging.
  	 *
  	 * @param args Command line arguments (not used).
  	 */
  	public static void main(final String[] args) {
  		System.out.println(data);
  	}
  }
  