package simulator;

/*
 * ContinuousRide.java
 * Created on Jan 9, 2005
 *
 */


/**
 *  ContinuousRide - This class implements the continuous rides at the amusement
 *  park.  These rides may dispatch cars that are empty, but can only dispatch
 *  at most 1 car per minute.  Example continuous rides are like the aerial tram.
 *  @author Ping
 *
 */
public class ContinuousRide extends Attraction
{
    public static String ATTRACTION_TYPE = "Continuous";
    
    // This constant describes the number of carloads of people in line 
    // before we have to add a car 
    protected final static int NUM_OF_CARLOADS_THRESHOLD = 5;
    
    // this is percentage of a sequence of empty cars out of all the cars before
    // we remove a car.
    protected final static int PERCENT_OF_EMPTY_CARS = 33;
    
    // This stores the number of empty cars launched in a row
    protected int numOfEmptyCars;

    /**
     * @param attractionName
     * @param entrance
     * @param exit
     * @param initialNumOfCars
     * @param maxNumOfCarsPossible
     * @param carCapacity
     * @param rideTime
     */
    public ContinuousRide(String attractionName, Location entrance, Location exit, 
            int initialNumOfCars, int maxNumOfCarsPossible, int carCapacity, 
            int rideTime)
    {
        // Note that continuous rides have no loading time, so we pass in 0.
        super(attractionName, entrance, exit, initialNumOfCars, maxNumOfCarsPossible,
                carCapacity, rideTime, 0);
    }
    /*
     * This function determines if we should add a car to the ride or not
     */
    protected boolean shouldAddCar()
    {
        int threshold = NUM_OF_CARLOADS_THRESHOLD * carCapacity;
        // we should add a car if our total capacity is less than the number of people waiting
        // and we haven't reached our full capacity in terms of number of cars
        return threshold < waitingLine.size() && cars.size() < maxNumOfCarsPossible;
    }
    
    /*
     * This function determines if we should remove a car from the attraction
     */
    protected boolean shouldRemoveCar()
    {
        int numCars = cars.size();
        // make it into a percentage, first multiply by 100 so we don't have to deal 
        // with double precision and rounding issues 
        int temp = numOfEmptyCars * 100;
        temp /= numCars;
        
        return temp > PERCENT_OF_EMPTY_CARS && numCars > 1;
        
    }
    
    /**
     * This function should be called when the attraction first opens
     *
     */
    public void openRide()
    {
        super.openRide();
        numOfEmptyCars = 0;
    }
    
    /**
     * This function returns the estimated waiting time for this ride.
     * It does not have to be 100% accurate but should present a rough 
     * estimate as to when someone will get on the ride (assuming he/she 
     * enters the waiting line right now).
     * 
     * @return int representing the expected number of minutes someone will wait
     */
    public int getEstimatedWaitTime()
    {
        if(timeSinceEstimate >= TIME_UNTIL_NEW_ESTIMATES)
        {
            timeSinceEstimate = 0;
            estimatedWaitTime = 0;
            try
            {
                ContinuousRide clone = (ContinuousRide)this.clone();
                clone.setSimulationMode(true);
                
                // the line not only needs to be empty, but there needs to be
                // a car available thats not on the ride already
                while(clone.getLineLength() > 0 || !clone.hasCarAvailable())
                {
                    clone.onClockTick();
                    estimatedWaitTime++;
                }
            }
            catch(CloneNotSupportedException e)
            {
                e.printStackTrace();
            }
        }
        return estimatedWaitTime;
    }
    
    public Object clone() throws CloneNotSupportedException
    {
        return super.clone();
    }
    
    /**
     * This function must remove customers from the waiting queue and put 
     * them on rides (if there are space available) or remove customer from rides 
     * (if they are done) during each clock tick.
     * 
     */
    public boolean onClockTick()
    {  
        // since we can only launch 1 car per turn, we should keep track
        // of whether we have launched a car or not.
        Car dispatchedCar = null;
                
        Customer[] riders;
        
        // we could use an iterator here, but we have more control if we do the
        // loop ourselves since we are adding/removing items from the list
        for(int i = 0; i < cars.size(); i++)
        {
            Car c = cars.get(i);
            if(c.isCarDone())
            {
                // unload the car
                c.unloadCustomers();
            }
            
            if(c.isCarWaiting() && dispatchedCar == null)
            {
                riders = extractCustomers();
                c.loadCustomers(riders); 
                
                // this car is ready to be launched, so lets remove it and 
                // put it in at the end.  This might always be the first element
                c.dispatchCar();
                
                if(c.isCarEmpty())
                {
                    // we just launchedan empty car, so add one to the sequence
                    numOfEmptyCars++;
                }
                else
                {
                    // we just launched a non-empty car. So the sequence is broken
                    numOfEmptyCars = 0;
                }
                
                // remove it so we can put it at the end
                dispatchedCar = cars.remove(i);
                
                // this is just to prevent the index from moving since the list 
                // has just shrunk so all the elements got "pushed" forward
                i--;
                
                //hasLoadedCar = true;
            }
            c.onClockTick();
        }
        if(dispatchedCar != null)
        {
            // add the dispatched car back to the end of the list
            // this is to model reality a little bit to make the conceptual 
            // model a little easier.  In reality when a car is dispatched it
            // really circles around to the back of all the other cars.
            cars.addLast(dispatchedCar);
        }
        
        updateNumOfCars();
    
        // this will update the stats at the end.
        return super.onClockTick();
    }
}
