package seatrading.main;

import java.awt.geom.Point2D;
import java.util.Random;
import seatrading.ships.Cruiser;
import seatrading.ships.Freighter;
import seatrading.ships.IShip;
import seatrading.ships.Tanker;

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

  /**
   * One random number generator for all my needs.
   */
  public static Random rnd = new Random();
  /**
   * Sea holds everything from ships to harbors.
   */
  static Sea sea = new Sea();
  /**
   * A global variable for keeping track of profits. Used concurrently in
   * Simulation.run() (read+write) and SeaTrading.main() (read)
   */
  public static volatile double profit;

  /**
   * Creates a sea, multiple harbors on its shore and spawns over 10 ships.
   * Measures the income of a shipping company in a simulation.
   *
   * Handles 27 different trade goods, and hundreds of thousands of containers!
   *
   * GLaDOS: Please do not attempt to remove testing apparatus from the testing
   * area.
   */
  public static void main(String[] args) {
    System.out.println("Creating famous actual harbors (but at random locations and on the same sea):");
    sea.addHarbor(new Harbor("Sydney Port", new Point2D.Double(rndCoord(), rndCoord())));
    sea.addHarbor(new Harbor("Victoria Seaport", new Point2D.Double(rndCoord(), rndCoord())));
    sea.addHarbor(new Harbor("Mumbai Bay", new Point2D.Double(rndCoord(), rndCoord())));
    sea.addHarbor(new Harbor("Port Falmouth", new Point2D.Double(rndCoord(), rndCoord())));
    sea.addHarbor(new Harbor("Shanghai International", new Point2D.Double(rndCoord(), rndCoord())));

    System.out.println("\nCreating famous cruisers (with mostly actual stats from Wikipedia and misc. research):");
    sea.addShip(new Cruiser("Titanic", 52.31, 2_435, rndHarbor()));
    sea.addShip(new Cruiser("Queen Elizabeth", 83.6, 2_283, rndHarbor()));
    sea.addShip(new Cruiser("Queen Mary II", 148.5, 2_620, rndHarbor()));
    sea.addShip(new Cruiser("Freedom of the Seas", 154.4, 3_634, rndHarbor()));
    sea.addShip(new Cruiser("Oasis of the Seas", 225.282, 6_296, rndHarbor()));

    System.out.println("\nCreating a well-known and some of the largest freighters (with actual stats again):");
    sea.addShip(new Freighter("Edmund Fitzgerald", 13_632.1, 30_690.3, rndHarbor()));
    sea.addShip(new Freighter("Fabiola", 140_259, 301_488.2, rndHarbor()));
    sea.addShip(new Freighter("TI Asia", 234_006, 441_893.3, rndHarbor()));
    sea.addShip(new Freighter("Colombo Express", 93_750.5, 104_400.2, rndHarbor()));
    sea.addShip(new Freighter("Ebba Maersk", 151_687.8, 156_907.6, rndHarbor()));
    sea.addShip(new Freighter("APL Southampton", 128_929.9, 131_358.1, rndHarbor()));
    sea.addShip(new Freighter("CSCL Star", 150_853.7, 155_470.7, rndHarbor()));

    System.out.println("\nCreating two of the largest oil tankers:");
    sea.addShip(new Tanker("AbQaiq", 100_000.0, 550_000, rndHarbor()));
    sea.addShip(new Tanker("Hellespont Alhambra", 90_000.0, 441_893, rndHarbor()));

    System.out.println("\nStarting every harbor with a random stock of goods:");
    for (Harbor h : sea.harbors) {
      h.initialLoad();
      h.printCurrentLoad();
    }

    System.out.print("\nStarting every freighter with a couple random goods");
    System.out.println(" (cruisers get passengers and tankers get oil, of course):");
    for (IShip ship : sea.ships) {
      ship.initialLoad();
      ship.printCurrentLoad();
    }

    System.out.print("\nGiving the company some starting cash: $");
    profit = round(rnd.nextDouble() * 100_000);
    double previousProfit = profit = round(profit);
    System.out.println(profit);

    System.out.println("\nBegin simulation!");
    new Thread(new Simulation()).start();

    while (true) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException ex) {
        System.err.println("Bye.");
      }

      if (rnd.nextInt(10) > 2) {
        sea.replenishGoods();
      }
      if (rnd.nextInt(10) > 6) {
        System.out.println("\nRandom harbor stats!");
        sea.harborStats();
      }
      if (rnd.nextInt(10) > 8) {
        System.out.println("\nRandom ship stats:");
        sea.shipStats();
      }

      if (previousProfit != profit) {
        profit = round(profit);
        previousProfit = profit;
        System.out.println("Profit update: $" + profit);
      }
    }
  }

  private static double rndCoord() {
    return round(rnd.nextDouble() * 10_000);
  }

  /**
   *
   * @return a randomly selected harbor from our sea.harbors list.
   */
  protected static Harbor rndHarbor() {
    return sea.harbors.get(rnd.nextInt(sea.harbors.size()));
  }

  /**
   * 1 decimal precision is more than enough for these tests. Accuracy is not a
   * goal. This yields much nicer output, although at a cost: So much rounding
   * makes the code ugly.
   *
   * BigDecimal would have been far superior.
   *
   * @param d the 8 decimals or more number to be mercilessly rounded
   * @return only the meaningful decimals are kept. In this case, just one.
   * @note When working with these rounded doubles, you have to re-round them
   * after each operation to keep their 1 decimal form.
   */
  public static double round(double d) {
    return Math.round(10 * d) / 10.0;
  }
}
//GLaDOS: Vital testing appratus destroyed...
