package simulator;

/*
 * IntervalRide.java
 * Created on Jan 9, 2005
 *
 */

import java.util.ListIterator;

/**
 *  IntervalRide - This class implements interval rides.  These rides are similar to 
 *  cycle rides except that it does not allow empty cars to be launched.  An excample
 *  would be a roller coaster ride.
 *  @author Ping
 *
 */
public class IntervalRide extends Attraction 
{
    public static String ATTRACTION_TYPE = "Interval";
    
    protected static final int NUM_OF_CARS_THRESHOLD = 1;
    protected static final int WAITING_THRESHOLD = 10;
    
    /**
     * @param attractionName
     * @param entrance
     * @param exit
     * @param initialNumOfCars
     * @param maxNumOfCarsPossible
     * @param carCapacity
     * @param rideTime
     * @param loadingTime
     */
    public IntervalRide(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()
    {
        int numOfCarsWaiting = 0;
        boolean shouldRemove = false;
        
        for(Car c : cars)
        {
            // get the waiting time for each car
            if(c.isCarWaiting() && 
               c.getMinutesSpent() > WAITING_THRESHOLD)
            {
                numOfCarsWaiting++;
                if(numOfCarsWaiting > NUM_OF_CARS_THRESHOLD)
                {
                    shouldRemove = true;
                    break;
                }
            }
        }
        return shouldRemove && cars.size() > 1;
        
    }
    
    /**
     * This function removes the first car that is waiting from the list  
     */
    protected void removeCar()
    {
        // find a car thats waiting so it can be removed
        // don't want to remove a car that is in the middle of the ride.
        ListIterator<Car> iterator = cars.listIterator();
        while(iterator.hasNext())
        {
            Car c = iterator.next();
            if(c.isCarWaiting())
            {
                iterator.remove();
                break;
            }
        }
    }
    
    
    /**
     * 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()
    {  
        Car dispatchedCar = null;
        // since we can only launch 1 car at a time, we only load
        // one car at a time.  Realistically, the rides probably only have
        // enough space to load 1 car at a time anyways.
        boolean hasLoadedCar = false;
        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() && !hasLoadedCar)
            {
                // During each clock tick, we are only loading 1 car
                riders = extractCustomers();
                if(riders.length != 0)
                {
                    // make sure we aren't loading an empty car
                    c.loadCustomers(riders);
                    hasLoadedCar = true;
                }
            }
            // we are only launching 1 car
            if(c.isCarReady() && dispatchedCar == null)
            {
                // 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();
                // 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--;
            }

            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();
    }

    /**
     * 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
            {
                IntervalRide clone = (IntervalRide)this.clone();
                clone.setSimulationMode(true);
                
                // 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
    {
        return super.clone();
    }
}