package simulator;


/*
 * CycleRide.java
 * Created on Jan 8, 2005
 *
 */

/**
 *  CycleRide - This class implements the cycle rides in the amusement park. 
 *  On cycle rides, everyone gets on and off at the same time like theater rides.
 *  @author Ping
 *
 */
public class CycleRide extends Attraction 
{
    public static String ATTRACTION_TYPE = "Cycle";
    
    // this is the number of previous cycles that we need to consider 
    protected final static int LAST_CYCLES = 3;
    protected final static int EMPTY_CAR_THRESHOLD = 2;
    // This is to keep track of the number of empty cars in the past X cycles
    protected int[] numOfEmptyCars = new int[LAST_CYCLES]; 
    
    /**
     * @param attractionName
     * @param entrance
     * @param exit
     * @param initialNumOfCars
     * @param maxNumOfCarsPossible
     * @param carCapacity
     * @param rideTime
     * @param loadingTime
     */
    public CycleRide(String attractionName, Location entrance, Location exit, 
            int initialNumOfCars, int maxNumOfCarsPossible, int carCapacity, 
            int rideTime, int loadingTime)
    {
        super(attractionName, entrance, exit, initialNumOfCars, maxNumOfCarsPossible,
                carCapacity, rideTime, loadingTime);
    }

    /*
     * This function determines if we should add a car to the ride or not
     */
    protected boolean shouldAddCar()
    {
        int numOfCars = cars.size();
        int capacity = numOfCars * 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 capacity < waitingLine.size() && numOfCars < maxNumOfCarsPossible;
    }
    
    /*
     * This function determines if we should remove a car from the attraction
     */
    protected boolean shouldRemoveCar()
    {
        boolean shouldRemove = true;
        for(int i : numOfEmptyCars)
        {
            // note: i is actually the value in the array, not the index
            if(i < EMPTY_CAR_THRESHOLD)
            {
                // there was a cycle where there are fewer than the threshold number of
                // empty cars
                shouldRemove = false;
                break;
            }
        }
        // we should keep at least 1 car on the ride.
        return shouldRemove && cars.size() > 1;
    }
    
    /**
     * This function should be called when the attraction first opens
     *
     */
    public void openRide()
    {
        super.openRide();
        // initialize the array to 0's
        for(int i = 0; i < numOfEmptyCars.length; i++)
        {
            numOfEmptyCars[i] = 0;
        }
    }
    
    /**
     * Thhis 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()
    {        
        /*
         * Note: this function is not implemented in the most efficient way.  
         * But I feel that it matches the mental model most effectively.  
         * I believe this particular way is easier to reason with (IMO)
         */
        
        if(cars.getFirst().isCarDone())
        {
            // all the cars are done. lets unload them all
            for(Car c : cars)
            {
                c.unloadCustomers();
            }
        }
        
        // the ride has finished 1 cycle, so lets see if we should add cars or remove them
        // its ok to just check the first car since this is a cycle ride, all cars
        // start and finish at the same time.
        if(cars.getFirst().isCarWaiting())
        {
            updateNumOfCars();
            
            Customer[] riders;
            boolean hasCustomers = false;
            
            int emptyCars = cars.size();
            // since the ride can start with empty cars, we need to
            // first go through and make sure there are NONE empty cars
            for(Car c : cars)
            {
                riders = extractCustomers();              
                if(riders.length != 0)
                {
                    // still have customers
                    emptyCars--;
                    hasCustomers = true;
                }
                // as long as there were some customers
                // we load the car (even if we load it as empty)
                if(hasCustomers)
                {
                    c.loadCustomers(riders);
                }
            }

            // record the number of empty cars for this cycle
            int lastIndex = numOfEmptyCars.length - 1;
            for(int i = 0; i < lastIndex; i++)
            {
                // bump the cycles "forward" such that the most recent cycle
                // is at the end of the array
                numOfEmptyCars[i] = numOfEmptyCars[i + 1];
            }
            numOfEmptyCars[lastIndex] = emptyCars;
        }
        
        if(cars.getFirst().isCarReady())
        {
            // all the cars are ready to be launched, so dispatch them all
            for(Car c : cars)
            {
                c.dispatchCar();
            }
        }
        
        // just process all the cars
        for(Car c : cars)
        {
            c.onClockTick();
        }
        // this will update the stats at the end.
        return super.onClockTick();
    }
    
    /**
     * 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
            {
                CycleRide clone = (CycleRide)this.clone();
                clone.setSimulationMode(true);
                
                // Car firstCar = clone.cars.getFirst();
                
                // the line not only needs to be empty, but there needs to be
                // a car available (if the car has just finished, u can
                // probably get on then)
                while(clone.getLineLength() > 0 || !clone.hasCarAvailable())
                {
                    clone.onClockTick();
                    estimatedWaitTime++;
                }
            }
            catch(CloneNotSupportedException e)
            {
                e.printStackTrace();
            }
        }
        
        return estimatedWaitTime;
    }
    
    public Object clone() throws CloneNotSupportedException
    {
        CycleRide clone = (CycleRide)super.clone();
        clone.numOfEmptyCars = new int[this.numOfEmptyCars.length];
        for(int i = 0; i < clone.numOfEmptyCars.length; i++)
        {
            clone.numOfEmptyCars[i] = this.numOfEmptyCars[i];
        }
        return clone;
    }
}

