public boolean marketBottom(CommandSender sender) {

    // gather the list of commodities
    List<Commodities> commodities = plugin.getDatabase().find(Commodities.class).findList();

    // sort 'em and return only the top 10
    List<Commodities> top10 =
        plugin
            .getDatabase()
            .filter(Commodities.class)
            .sort("value asc")
            .maxRows(10)
            .filter(commodities);

    // calculate elasticity
    List<Integer> elasticities = new LinkedList<Integer>();

    for (int index = 0; index < top10.size(); index++) {

      Commodities c = top10.get(index);
      double value = c.getValue();
      double changeRate = c.getChangeRate();
      double minValue = c.getMinValue();
      if (value < minValue) {

        double newValue = Math.abs(value - minValue);
        double elasticity = (newValue / changeRate);
        int el = (int) Math.round(elasticity);

        elasticities.add(index, el);
        c.setValue(minValue);
      } else {
        elasticities.add(index, 0);
      }
    }

    // Send them to the player
    for (int x = 0; x < top10.size(); x++) {
      int rank = x + 1;

      sender.sendMessage(
          ChatColor.GREEN
              + String.valueOf(rank)
              + ". "
              + ChatColor.WHITE
              + top10.get(x).getName()
              + " "
              + ChatColor.GRAY
              + top10.get(x).getValue()
              + " "
              + ChatColor.DARK_GREEN
              + elasticities.get(x));
    }

    return true;
  }
  /**
   * Determine the cost of a given number of an item and calculate a new value for the item
   * accordingly.
   *
   * @param oper 1 for buying, 0 for selling.
   * @param commodity the commodity in question
   * @param amount the desired amount of the item in question
   * @return the total cost and the calculated new value as an Invoice
   */
  public Invoice generateInvoice(int oper, Commodities commodity, int amount) {

    // get the initial value of the item, 0 for not found
    Invoice inv = new Invoice(0.0, 0.0);
    inv.value = commodity.getValue();

    // get the spread so we can do one initial decrement of the value if we are selling
    BigDecimal spread = BigDecimal.valueOf(commodity.getSpread());

    // determine the total cost
    inv.total = 0.0;

    for (int x = 1; x <= amount; x++) {
      BigDecimal minValue = BigDecimal.valueOf(commodity.getMinValue());
      BigDecimal maxValue = BigDecimal.valueOf(commodity.getMaxValue());
      BigDecimal changeRate = BigDecimal.valueOf(commodity.getChangeRate());

      // work the spread on the first one.
      if ((oper == 0) && (x == 1)) {
        inv.subtractValue(spread.doubleValue());
      } else if ((oper == 0) && (x > 1)) { // otherwise, do the usual decriment.
        inv.subtractValue(changeRate.doubleValue());
      }

      // check the current value
      if (inv.getValue() >= minValue.doubleValue()) { // current value is @ or above minValue
        // be sure value is not above maxValue
        if (inv.getValue() < maxValue.doubleValue()) { // current value is "just right"
          inv.addTotal(inv.getValue()); // add current value to total
        } else { // current value is above the max
          inv.addTotal(maxValue.doubleValue()); // add maxValue to total
        }
      } else { // current value is below the minimum

        inv.addTotal(minValue.doubleValue()); // add the minimum to total

        if ((oper == 0) && (x == 1)) {
          inv.subtractTotal(
              spread
                  .doubleValue()); // subtract the spread if we're selling and this is the first run
        }
      }

      // Change our stored value for the item
      // we don't care about min/maxValue here because we don't want the value to 'bounce' off of
      // them.
      if (oper == 1) {
        inv.addValue(changeRate.doubleValue());
      }
    }
    return inv;
  }
  public BigDecimal price(String item) {

    // retrieve the commodity in question
    Commodities commodity =
        plugin.getDatabase().find(Commodities.class).where().ieq("name", item).findUnique();

    if (commodity == null) return null;

    double price = commodity.getValue();
    double minValue = commodity.getMinValue();
    double maxValue = commodity.getMaxValue();

    if (price > maxValue) return BigDecimal.valueOf(maxValue).setScale(2, RoundingMode.HALF_UP);
    if (price < minValue) return BigDecimal.valueOf(minValue).setScale(2, RoundingMode.HALF_UP);
    return BigDecimal.valueOf(price).setScale(2, RoundingMode.HALF_UP);
  }
  /**
   * Sell a specified amount of an item for the player.
   *
   * @param player The player on behalf of which these actions will be carried out.
   * @param item The desired item in the form of the item name.
   * @param amount The desired amount of the item to sell.
   * @return true on success, false on failure.
   */
  public boolean sell(Player player, String item, int amount) {

    // Be sure we have a positive amount
    if (amount < 1) {
      player.sendMessage(ChatColor.RED + "Invalid amount.");
      player.sendMessage("No negative numbers or zero, please.");
      return false;
    }

    // retrieve the commodity in question
    Commodities commodity =
        plugin.getDatabase().find(Commodities.class).where().ieq("name", item).findUnique();

    if (commodity == null) {
      player.sendMessage(ChatColor.RED + "Not allowed to buy that item.");
      player.sendMessage("Be sure you typed the correct name");
      return false;
    }

    // determine what it will pay
    Invoice invoice = generateInvoice(0, commodity, amount);

    // If the player has enough of the item, perform the transaction.
    int id = commodity.getNumber();
    Byte byteData = Byte.valueOf(String.valueOf(commodity.getData()));

    ItemStack its = new ItemStack(id, amount, (short) 0, byteData);

    if (player.getInventory().contains(id)) {

      // Figure out how much is left over
      int left = getAmountInInventory(player, its) - amount;

      if (left < 0) { // this indicates the correct id, but the wrong bytedata value
        // give nice output even if they gave a bad name.
        player.sendMessage(ChatColor.RED + "You don't have enough " + item);
        player.sendMessage(
            ChatColor.GREEN
                + "In Inventory: "
                + ChatColor.WHITE
                + getAmountInInventory(player, its));
        player.sendMessage(ChatColor.GREEN + "Attempted Amount: " + ChatColor.WHITE + amount);
        return false;
      }

      // Take out all of the item
      int x = 0;

      for (@SuppressWarnings("unused")
      ItemStack stack :
          player
              .getInventory()
              .getContents()) { // we do it this way incase a user has an expanded inventory via
                                // another plugin

        ItemStack slot = player.getInventory().getItem(x);

        if (slot != null) {
          Byte slotData = Byte.valueOf("0");

          try {
            slotData = slot.getData().getData();
          } catch (NullPointerException e) {
          }

          if ((slot.getTypeId() == id) && (slotData.compareTo(byteData) == 0)) {
            player.getInventory().clear(x);
          }
        }
        x++;
      }

      // put back what was left over
      if (left > 0) {
        ItemStack itsLeft = its;
        itsLeft.setAmount(left);
        player.getInventory().addItem(itsLeft);
      }

      // record the change in value
      commodity.setValue(invoice.getValue());
      plugin.getDatabase().save(commodity);

      // use BigDecimal to format value for output
      double v = commodity.getValue();
      double max = commodity.getMaxValue();
      double min = commodity.getMinValue();
      BigDecimal value;
      if (v < max && v > min) {
        value = BigDecimal.valueOf(v).setScale(2, RoundingMode.HALF_UP);
      } else if (v <= min) {
        value = BigDecimal.valueOf(min).setScale(2, RoundingMode.HALF_UP);
      } else {
        value = BigDecimal.valueOf(max).setScale(2, RoundingMode.HALF_UP);
      }
      BigDecimal spread = BigDecimal.valueOf(commodity.getSpread());

      // give some nice output
      BigDecimal sale =
          BigDecimal.valueOf((invoice.getTotal() + spread.doubleValue()))
              .setScale(2, RoundingMode.HALF_UP);

      player.sendMessage(ChatColor.GREEN + "--------------------------------");
      player.sendMessage(
          ChatColor.GREEN
              + "Old Balance: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(economy.getBalance(player.getName()))
                  .setScale(2, RoundingMode.HALF_UP));

      // deposit the money
      economy.depositPlayer(player.getName(), invoice.getTotal());

      player.sendMessage(ChatColor.GREEN + "Sale: " + ChatColor.WHITE + sale);
      player.sendMessage(ChatColor.GREEN + "Selling Fee: " + ChatColor.WHITE + spread);
      player.sendMessage(ChatColor.GREEN + "--------------------------------");
      player.sendMessage(
          ChatColor.GREEN
              + "Net Gain: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(invoice.getTotal()).setScale(2, RoundingMode.HALF_UP));
      player.sendMessage(
          ChatColor.GREEN
              + "New Balance: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(economy.getBalance(player.getName()))
                  .setScale(2, RoundingMode.HALF_UP));
      player.sendMessage(ChatColor.GREEN + "--------------------------------");
      player.sendMessage(
          ChatColor.GRAY + item + ChatColor.GREEN + " New Price: " + ChatColor.WHITE + value);
      return true;
    } else { // give nice output even if they gave a bad number.

      player.sendMessage(ChatColor.RED + "You don't have enough " + item);
      player.sendMessage(
          ChatColor.GREEN + "In Inventory: " + ChatColor.WHITE + getAmountInInventory(player, its));
      player.sendMessage(ChatColor.GREEN + "Attempted Amount: " + ChatColor.WHITE + amount);
      return false;
    }
  }
  /**
   * Buy a specified amount of an item for the player.
   *
   * @param player The player on behalf of which these actions will be carried out.
   * @param item The desired item in the form of the item name.
   * @param amount The desired amount of the item to purchase.
   * @return true on success, false on failure.
   */
  public boolean buy(Player player, String item, int amount) {

    // Be sure we have a positive amount
    if (amount < 1) {
      player.sendMessage(ChatColor.RED + "Invalid amount.");
      player.sendMessage("No negative numbers or zero, please.");
      return false;
    }

    // retrieve the commodity in question
    Commodities commodity =
        plugin.getDatabase().find(Commodities.class).where().ieq("name", item).findUnique();

    // check that we found something
    if (commodity == null) {
      player.sendMessage(ChatColor.RED + "Not allowed to buy that item.");
      player.sendMessage("Be sure you typed the correct name");
      return false;
    }

    // determine what it will cost
    Invoice invoice = generateInvoice(1, commodity, amount);

    // check the player's wallet
    if (economy.has(player.getName(), invoice.getTotal())) {

      // give 'em the items and drop any extra
      Byte byteData = Byte.valueOf(String.valueOf(commodity.getData()));
      int id = commodity.getNumber();

      HashMap<Integer, ItemStack> overflow =
          player.getInventory().addItem(new ItemStack(id, amount, (short) 0, byteData));
      for (int a : overflow.keySet()) {
        player.getWorld().dropItem(player.getLocation(), overflow.get(a));
      }

      // save the new value
      commodity.setValue(invoice.getValue());
      getDatabase().save(commodity);

      // use BigDecimal to format value for output
      double v = commodity.getValue();
      double max = commodity.getMaxValue();
      double min = commodity.getMinValue();
      BigDecimal value;
      if (v < max && v > min) {
        value = BigDecimal.valueOf(v).setScale(2, RoundingMode.HALF_UP);
      } else if (v <= min) {
        value = BigDecimal.valueOf(min).setScale(2, RoundingMode.HALF_UP);
      } else {
        value = BigDecimal.valueOf(max).setScale(2, RoundingMode.HALF_UP);
      }

      // Give some nice output.
      player.sendMessage(ChatColor.GREEN + "--------------------------------");
      player.sendMessage(
          ChatColor.GREEN
              + "Old Balance: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(economy.getBalance(player.getName()))
                  .setScale(2, RoundingMode.HALF_UP));
      // Subtract the invoice (this is an efficient place to do this)
      economy.withdrawPlayer(player.getName(), invoice.getTotal());

      player.sendMessage(
          ChatColor.GREEN
              + "Cost: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(invoice.getTotal()).setScale(2, RoundingMode.HALF_UP));
      player.sendMessage(
          ChatColor.GREEN
              + "New Balance: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(economy.getBalance(player.getName()))
                  .setScale(2, RoundingMode.HALF_UP));
      player.sendMessage(ChatColor.GREEN + "--------------------------------");
      player.sendMessage(
          ChatColor.GRAY + item + ChatColor.GREEN + " New Price: " + ChatColor.WHITE + value);
      return true;
    } else { // Otherwise, give nice output anyway ;)

      // The idea here is to show how much more money is needed.
      BigDecimal difference =
          BigDecimal.valueOf(economy.getBalance(player.getName()) - invoice.getTotal())
              .setScale(2, RoundingMode.HALF_UP);
      player.sendMessage(ChatColor.RED + "You don't have enough money");
      player.sendMessage(
          ChatColor.GREEN
              + "Balance: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(economy.getBalance(player.getName()))
                  .setScale(2, RoundingMode.HALF_UP));
      player.sendMessage(
          ChatColor.GREEN
              + "Cost: "
              + ChatColor.WHITE
              + BigDecimal.valueOf(invoice.getTotal()).setScale(2, RoundingMode.HALF_UP));
      player.sendMessage(ChatColor.GREEN + "Difference: " + ChatColor.RED + difference);
      return true;
    }
  }
  /**
   * see CommandHelper.marketAdd() for now
   *
   * @param sender
   * @param args
   * @return
   */
  private boolean marketAdd(CommandSender sender, String[] args) {

    String addendNumber = null,
        addendData = null,
        addendName = null,
        addendValue = null,
        addendMinValue = null,
        addendMaxValue = null,
        addendChangeRate = null,
        addendSpread = null;
    try {

      addendNumber = args[1];
      addendData = args[2];
      addendName = args[3];
      addendValue = args[4];
      addendMinValue = args[5];
      addendMaxValue = args[6];
      addendChangeRate = args[7];
      addendSpread = args[8];

    } catch (ArrayIndexOutOfBoundsException e) {

      new CommandHelper("Too few arguments", sender).marketAddHelp();
      return false;
    }

    Commodities commodity = new Commodities();

    try {

      commodity.setNumber(Integer.valueOf(addendNumber));
      commodity.setData(Integer.valueOf(addendData));
      commodity.setName(addendName);
      commodity.setValue(Double.valueOf(addendValue));
      commodity.setMaxValue(Double.valueOf(addendMaxValue));
      commodity.setMinValue(Double.valueOf(addendMinValue));
      commodity.setChangeRate(Double.valueOf(addendChangeRate));
      commodity.setSpread(Double.valueOf(addendSpread));

    } catch (NumberFormatException e) {

      new CommandHelper("Invalid Arguments", sender).marketAddHelp();
      return false;
    }
    System.out.println(commodity.toString());
    try {

      new CommodityDBAdder(this).addCommodity(commodity);

    } catch (DuplicateCommodityException e) {

      sender.sendMessage(e.getMessage());
      return false;
    }

    sender.sendMessage(commodity.getName() + " successfully added to the database");

    return true;
  }
  /** use this in the case that we need to update from the old way of storing data in config.yml */
  @SuppressWarnings("deprecation")
  private void switchToDatabase() {

    logger.info("[" + pdfFile.getName() + "] Converting flatfile to database...");

    // load old config file
    org.bukkit.configuration.Configuration items = plugin.getConfig();

    // populate the database with existing values
    logger.info("[" + plugin.getDescription().getName() + "] Populating database ...");
    for (String item : items.getKeys(false)) {

      Commodities commodity =
          plugin
              .getDatabase()
              .find(Commodities.class)
              .where()
              .ieq("name", item)
              .ieq("number", items.getString(item + ".number"))
              .findUnique();

      if (commodity == null) {

        commodity = new Commodities();
        commodity.setName(item);

        for (String key : items.getConfigurationSection(item).getKeys(false)) {

          String value = items.getString(item + "." + key);

          if (key.equalsIgnoreCase("value")) commodity.setValue(Double.valueOf(value));
          if (key.equalsIgnoreCase("number")) commodity.setNumber(Integer.valueOf(value));
          if (key.equalsIgnoreCase("minValue")) commodity.setMinValue(Double.valueOf(value));
          if (key.equalsIgnoreCase("maxValue")) commodity.setMaxValue(Double.valueOf(value));
          if (key.equalsIgnoreCase("changeRate")) commodity.setChangeRate(Double.valueOf(value));
          if (key.equalsIgnoreCase("data")) commodity.setData(Integer.valueOf(value));
          if (key.equalsIgnoreCase("spread")) commodity.setSpread(Double.valueOf(value));
        }
      } else {
        logger.warning(
            "["
                + pdfFile.getName()
                + "] Duplicate commodity found, that can't be good. You may want to restore the config.yml backup, delete Dynamark.db (or equivilant), then check the file for commodities with the same \"number\", correct the issue, and then restart your server to try again.");
        continue;
      }

      plugin.getDatabase().save(commodity);
      commodity = null;
    }

    // mv config.yml to config.yml.bak
    logger.info("[" + plugin.getDescription().getName() + "] backing up config.yml...");

    File configFlatFile = new File(directory + File.separator + "config.yml");
    File backupName = new File(directory + File.separator + "config.yml.bak");

    configFlatFile.renameTo(backupName);

    //  rm config.yml.example
    File toDelete = new File(directory + File.separator + "config.yml.EXAMPLE");

    toDelete.delete();

    logger.info(
        "[" + plugin.getDescription().getName() + "] Successfully converted flatfile to database");
  }