package edu.unm.cs.cs351.tdrl.f07.p2.test;

import static java.lang.System.out;
import edu.unm.cs.cs351.tdrl.f07.p2.CIT;
import edu.unm.cs.cs351.tdrl.f07.p2.ConcrReceipt;
import edu.unm.cs.cs351.tdrl.f07.p2.MoMoneyWorld;
import edu.unm.cs.cs351.tdrl.f07.p2.DiscrEventSimulator;
import edu.unm.cs.cs351.tdrl.f07.p2.EmployeeType;
import edu.unm.cs.cs351.tdrl.f07.p2.OrderReceipt;
import edu.unm.cs.cs351.tdrl.f07.p2.World;
import edu.unm.cs.cs351.tdrl.f07.p2.XRandom;

/**
 * A small demonstration class that illustrates the interaction
 * loop for getting data from a {@link World} object.  This
 * isn't a full simulation, so it cheats by delivering each order
 * instantly.  Heh heh.
 * <p>
 * This uses the type {@link String} for the receipt type.
 * 
 * @author terran
 * @version 1.0
 */
public class DemoWorld {

  public static void main(String[] args) {
    // First, create a new World object.  In this case, the
    // CruelHarshWorld requires a random number source.  We initialize
    // the prng with a fixed seed here to ensure repeatable testing.
    // If you want to see this in a more "realistic" scenario, remove
    // the constant from the XRandom constructor and it will seed
    // from the current time.
    final World<String> w=new MoMoneyWorld<String>(new XRandom(69));
    // Start our simulation's clock at 0
    int now=0;
    // We need to "boot the World" by acknowledging the receipt
    // of an order that never was.  Note: passing null into
    // the World a second time is an error.
    int nextOrderTime=w.acknowledgeOrder(null,now);
    // Now we enter the order retrieval and "processing" loop.
    // We'll run up to 200000 steps (about 14 days) for kicks.
    final int maxSteps=200000;
    int ordersProcessed=0;
    int newCount=0;
    int changeCount=0;
    int cancelCount=0;
    double balance=0.0;
    while (now<maxSteps) {
      // Advance time to the next time that something happens.
      // In this case, we're not maintaining a full simulation with
      // queues and all that gunk, so the only "next thing" that
      // can happen is a new order arriving.
      now=nextOrderTime;
      // Ask the world for the thing that arrives at this clock
      // tick.
      CIT<String> cust=w.getOrderAtTime(now);
      assert now==cust.getArrivalTime();
      // keep some statistics
      if (cust.getPrevTransactionReceipt()==null) {
        ++newCount;
      }
      else {
        if (cust.isCancellation()) {
          ++cancelCount;
        }
        else {
          ++changeCount;
        }
      }
      // Generate a receipt for this order
      OrderReceipt<String> r=new ConcrReceipt(cust.getCustomerID());
      // Let the customer know that we got the order and,
      // simultaneously, find out when the next order arrival
      // time is.
      nextOrderTime=w.acknowledgeOrder(r,now);
      // Instant gratification!
      w.deliver(now+1,r,cust.getOrder(),false);
      // money we made on delivering this computer
      balance+=(cust.getOrder().getSalePrice()-
                cust.getOrder().getFixedPrice());
      ++ordersProcessed;
      // an intermediate print-out, just to show what's going on
      if ((ordersProcessed % 50)==0) {
        out.println("[t=" + now + "] " + cust);
      }
    }
    out.println("Welcome to McFriendly's!  Over " +
                       (ordersProcessed-1) + " served!");
    out.println("\t" + newCount + " new orders");
    out.println("\t" + changeCount + " change orders");
    out.println("\t" + cancelCount + " cancellation orders");
    out.println("Performance report:");
    // This should use a real DiscrEventSim object for the report.
    // For this small demo program, though, we'll just use a stub
    // object whose only job is to report some statistics.
    // First we need to figure out the final balance, which is the
    // income (calculated on a per-order basis, above) less the
    // salary expenses.
    _StubSim s=new _StubSim(now);
    for (EmployeeType t : EmployeeType.values()) {
      balance-=s.getEmployeeCount(t)*(now*(t.getHourlyRate()/60.0));
    }
    s.setBalance(balance);
    out.println(w.finalReport(s));
  }

  /* ****************** end of public stuff ***************** */
  
  /**
   * A stub for the {@link DiscrEventSimulator} interface.  This
   * doesn't implement any kind of real simulation -- it exists only
   * to provide a small amount of data to the {@link World#finalReport(DiscrEventSimulator)}
   * method.
   * 
   * @author terran
   * @version 1.0
   */
  private static class _StubSim implements DiscrEventSimulator<String> {
    public _StubSim(int time) {
      _time=time;
      _balance=0.0;
    }
    public void run(World<String> w, int tmax) {}
    public void setBalance(double b) {
      _balance=b;
    }
    public int getCurrentTime() {
      return _time;
    }
    public int getEmployeeCount(EmployeeType t) {
      return 1;
    }
    public double getCurrentBalance() {
      return _balance;
    }
    
    private final int _time;
    private double _balance;
  }
}
