package seatrading.ships;

import seatrading.main.Harbor;
import seatrading.main.SeaTrading;

/**
 * @author Daniel "3ICE" Berezvai
 */
public abstract class Ship {

  private String name;
  protected double weight;
  protected Harbor harbor;
  private volatile boolean atHarbor;
  private volatile boolean canTrade;
  protected int fuel;
  protected final int fuelCapacity;

  public Ship(String name, double weight, Harbor homeHarbor) {
    this.name = name;
    this.weight = weight;
    harbor = homeHarbor;
    atHarbor = canTrade = true;
    fuel = fuelCapacity = 100 + (int) weight;
  }

  public abstract double getTotalWeight();

  public String getName() {
    return name;
  }

  /**
   * If fuel levels are below 80% and the harbor has oil in stock, refuel the
   * ship.
   *
   * @return price
   */
  public double reFuel() {
    int deficit = fuelCapacity - fuel;
    int successfullyBoughtAmount = 0;
    if (deficit > fuelCapacity * .8) {
      successfullyBoughtAmount = harbor.buyFuel(deficit);
      fuel += successfullyBoughtAmount;
    }
    return successfullyBoughtAmount * 100;
  }

  public Harbor getHarbor() {
    return harbor;
  }

  public boolean isAtHarbor() {
    return atHarbor;
  }

  public void arriveAtHarbor(Harbor harbor) {
    this.atHarbor = true;
    this.harbor = harbor;
  }

  public void leaveHarbor() {
    this.atHarbor = false;
  }

  public void canNotTradeNextRound() {
    this.canTrade = false;
  }

  public void canTradeNextRound() {
    this.canTrade = true;
  }

  public boolean canTrade() {
    return canTrade;
  }

  /**
   * Checks if there is enough fuel for the trip. Travelling is done in a
   * separate thread.
   *
   * @param h the target harbor
   * @return success or failure
   * @see sailTo()
   */
  public boolean setSailTo(Harbor h) {
    int cost = SeaTrading.rnd.nextInt(1 + (int) ((getTotalWeight() / 100_000.0)
            * (harbor.getDistanceFrom(h) / 100.0)));
    if (fuel >= cost) {
      fuel -= cost;
      leaveHarbor();
      new Thread(new Sailer(this, h, cost)).start();
      return true;
    }
    //System.err.println("cost = " + cost + "  fuel = " + fuel);
    return false;
  }

  private static class Sailer implements Runnable {

    Ship aThis;
    Harbor target;
    long cost;

    public Sailer(Ship aThis, Harbor to, int cost) {
      this.aThis = aThis;
      this.target = to;
      this.cost = cost;
    }

    public void run() {
      try {
        Thread.sleep(1000 + cost * 100);
      } catch (InterruptedException ex) {
        System.err.println("I wasn't done sailing!");
      }
      System.out.println("Sailing from " + aThis.harbor.getName() + " to "
              + target.getName() + " took " + aThis.getName() + " " + cost + " hours.");
      aThis.arriveAtHarbor(target);
    }
  }
}