/***************************************************************
 * RailYard.java   -- CS100m-fa05 Project 6
 *
 * A simulation of queue operations with rail tracks, each of which is a CarQueue
 * representing the cars sitting on the track.
 *
 *
 * @ TO STUDENTS
 *
 * Complete methods with comments "### Implement this method ###" and write
 * your code in the specified place. Do not modify any other part of the
 * program unless instructed to do so.
 *
 * You may NOT import any classes.
 *
 *
 * @ Yunpeng Li, Nov. 17, 2005
 */

public class RailYard {
    // --- Variables ---
    protected CarQueue[] tracks;  // a set of tracks, i.e. an array of CarQueue


    // --- Constructors ---

    /* Construct a RailYard with numTracks rail tracks, i.e. CarQueue objects.
     All except the last one are normal queues; while the last one should
     be constructed as a doubly-ended queue. Each track should have an initial
     capacity initCapacity.
     */
    public RailYard(int numTracks, int initCapacity) {
        if(numTracks <= 0)
            throw new IllegalArgumentException("numTracks need to be positive");
        // ### Implement this method ###
        //--------- YOUR CODE BELOW ---------
        tracks = new CarQueue[numTracks];
        for(int i=0; i<numTracks; i++) {
            if(i == numTracks - 1)
                tracks[i] = new CarDEQueue(initCapacity);
            else
                tracks[i] = new CarQueue(initCapacity);
        }
        //--------- YOUR CODE ABOVE ---------
    }

    /* Construct a RailYard with numTracks rail tracks, same as the previous
     constructor, except for that each track is contructed with
     the default initial capacity.
     Hint: Use keyword 'this'.
     */
    public RailYard(int numTracks) {
        // ### Implement this method ###
        //--------- YOUR CODE BELOW ---------
        this(numTracks, CarQueue.DEF_INIT_CAPACITY);
        //--------- YOUR CODE ABOVE ---------
    }


    // --- Methods ---

    /* =Return the number of tracks in this RailYard object.
     */
    public int size() {
        return tracks.length;
    }

    /* =Return the number of cars on track i.
     */
    public int sizeOfTrack(int i) {
        return tracks[i].size();
    }

    /* ={Track i is empty (has no car)}, true or false
     */
    public boolean isEmptyTrack(int i) {
        return tracks[i].isEmpty();
    }

    /* Add a car to the rear of track i.
     */
    public void addRear(Car obj, int i) {
        tracks[i].insertLast(obj);
    }

    /* =Return the car at the front of track i without removing it.
     */
    public Car front(int i) {
        return tracks[i].first();
    }

    /* =Remove a car from the front of track i and return the Car object.
     */
    public Car removeFront(int i) {
        return tracks[i].removeFirst();
    }

    /* Add a car to the front of the track that is a double ended queue.
     */
    public void addFront(Car obj) {
        // ### Implement this method ###
        //--------- YOUR CODE BELOW ---------
        ((CarDEQueue)tracks[tracks.length-1]).insertFirst(obj);
        //--------- YOUR CODE ABOVE ---------
    }

    /* =Return, without removing, the car at the rear end of the track that
     is a double ended queue.
     */
    public Car rear() {
        // ### Implement this method ###
        //--------- YOUR CODE BELOW ---------
        return ((CarDEQueue)tracks[tracks.length-1]).last();
        //--------- YOUR CODE ABOVE ---------
    }

    /* =Remove and return the car at the rear end of the track that is
     a double ended queue.
     */
    public Car removeRear() {
        // ### Implement this method ###
        //--------- YOUR CODE BELOW ---------
        return ((CarDEQueue)tracks[tracks.length-1]).removeLast();
        //--------- YOUR CODE ABOVE ---------
    }


    // --- Simulation ---

    // Instructions
    public static final int
            ADD_FRONT    = 0,
            ADD_REAR     = 1,
            REMOVE_FRONT = 2,
            REMOVE_REAR  = 3,
            GET_FRONT    = 4,
            GET_REAR     = 5;

    // A simple structure to hold an instruction, a Car object, and a track #
    private static final class Instruction {
        public int ins;
        public Car obj;
        public int tn;

        public Instruction(int _ins, Car _obj, int _tn) {
            ins = _ins;
            obj = _obj;
            tn = _tn;
        }
    }

    /* Perform a simulation step according to instruction ins with data obj
     * carried out on track i.
     */
    public void simulateStep(int ins, Car obj, int i) {
        String cs;  // A String representation of a Car
        
        /* A switch statement is a kind of selection statement (like the if 
         * statement).  In the example below, if variable ins has the value
         * ADD_FRONT, then the set of statements indented after "case" ADD_FRONT
         * is executed.  If variable ins has the value ADD_REAR, then the set
         * of statements indented after case ADD_REAR is executed, and so
         * forth.  You do not need to learn (for the exam) the switch statement,
         * but we would like you to see this example and know of the switch 
         * statement.
         */
                
        switch(ins) {
            case ADD_FRONT:  // does not need track number parameter
                addFront(obj);
                System.out.println("ADD_FRONT Car" + obj);
                break;
            case ADD_REAR:
                addRear(obj, i);
                System.out.println("ADD_REAR Car" + obj + " to Track " + i);
                break;
            case REMOVE_FRONT:  // does not need parameter obj
                try { cs = "Car" + removeFront(i); }
                catch(DataEmptyException e) { cs = "<TRACK EMPTY>"; }
                System.out.println("REMOVE_FRONT from Track " + i +
                                   " returned " + cs);
                break;
            case REMOVE_REAR:  // needs neither obj nor i
                try { cs = "Car" + removeRear(); }
                catch(DataEmptyException e) { cs = "<TRACK EMPTY>"; }
                System.out.println("REMOVE_REAR returned " + cs);
                break;
            case GET_FRONT:  // doesn't need parameter obj
                try { cs = "Car" + front(i); }
                catch(DataEmptyException e) { cs = "<TRACK EMPTY>"; }
                System.out.println("GET_FRONT from Track " + i +
                                   " returned " + cs);
                break;
            case GET_REAR:  // needs neither obj nor i
                try { cs = "Car" + rear(); }
                catch(DataEmptyException e) { cs = "<TRACK EMPTY>"; }
                System.out.println("GET_REAR returned " + cs);
                break;
            default:
                System.out.println("** Unknown instruction (" + ins +
                                   ") - ignored");
        }
    }

    /* Main method -- Simulation
     */
    public static void main(String[] args) {
        final int NA = -1;  // indicates a dummy (not used) actual parameter
        // --- Simulation Data ---
        /* You are recommended to also create your own simulation data for more
          thorough tests. PASSING THE FOLLOWING SIMULATION IS A NECESSARY
          BUT NOT SUFFICIENT CONDITION FOR THE CORRECTNESS OF YOUR CODE.
         */
        final int n = 2;  // number of tracks
        final int cap = 50;  // initial capacity of each track
        final Instruction[] insSeq = {  // simulation instruction sequence
                new Instruction(ADD_REAR, new Car(), 0),
                new Instruction(ADD_REAR, new Car(), 0),
                new Instruction(REMOVE_FRONT, new Car(), 0),
                new Instruction(ADD_REAR, new Car(), 0),
                new Instruction(REMOVE_FRONT, new Car(), 0),
                new Instruction(REMOVE_FRONT, new Car(), 0),
                new Instruction(GET_FRONT, new Car(), 0),
                new Instruction(ADD_REAR, new Car(), 1),
                new Instruction(ADD_REAR, new Car(), 1),
                new Instruction(ADD_FRONT, new Car(), NA),
                new Instruction(ADD_FRONT, new Car(), NA),
                new Instruction(REMOVE_REAR, new Car(), NA),
                new Instruction(REMOVE_REAR, new Car(), NA),
                new Instruction(REMOVE_REAR, new Car(), NA),
                new Instruction(GET_REAR, new Car(), NA),
                new Instruction(GET_FRONT, new Car(), 1),
                new Instruction(REMOVE_FRONT, new Car(), 1),
                new Instruction(REMOVE_REAR, new Car(), NA)
        };

        // --- Simulation data structure and code ---
        RailYard yard = new RailYard(n, cap);
        System.out.println("--- Rail yard has " + yard.size() + " tracks ---");
        for(int i=0; i<insSeq.length; i++) {
            Instruction insSet = insSeq[i];
            yard.simulateStep(insSet.ins, insSet.obj, insSet.tn);
        }
    }
}
