import java.util.Random;

class PostOffice {

    private ServicePosition sp;
    private QueueOfCustomers queue;
    private double probabilityOfArrival;
    private Random randy;
    
    private int numCustomersWhoGotServed;
    private int numCustomersWhoTurnedAway;
    private int totalCustomerWaitingTime;
    private int servicePositionIdleTime;
    private int servicePositionBusyTime;
    private int[] waitingFrequencies;
    
    public PostOffice(int capacity, double probabilityOfArrival, int minServiceTime, int maxServiceTime) {
        // Set up the post office with one queue and one service position
        sp = new ServicePosition(minServiceTime, maxServiceTime);
        queue = new QueueOfCustomers(capacity);
        this.probabilityOfArrival = probabilityOfArrival;
        randy = new Random();
        waitingFrequencies = new int[capacity * maxServiceTime];
    }

    public void run(int period, boolean isVerbose) {

        int customerId = 0;
        
        // Each loop cycle deals with one unit of time
        
        for (int time = 0; time < period; time++) {
        
            // If someone is at the service position but their transaction is over, 
            // then finish serving them
            if (sp.isBusy() && sp.isTransactionOver(time)) {
	            sp.finishServing();
	            numCustomersWhoGotServed++;
            }

            // Then, if no one is at the service position but there are people in the queue, 
            // start serving the person at the front of the queue
            if (! sp.isBusy() && ! queue.isEmpty()) {
                Customer luckyBoy = queue.getFront();
	            sp.startServing(luckyBoy, time);
                queue.remove();
                int luckyBoyWaitingTime = luckyBoy.getWaitingTime(time);
                totalCustomerWaitingTime += luckyBoyWaitingTime; 
                waitingFrequencies[luckyBoyWaitingTime]++;
            }
            
            // By this point, we know whether you're busy in this time period or not
            if (sp.isBusy()) {
                servicePositionBusyTime++;
            } else {
                servicePositionIdleTime++;
            }

            // Also work out whether a new customer has arrived 
            // If they have but the queue is full, they turn away; 
            // if the queue isn't full, they join it
            if (customerHasArrived()) {
	            if (queue.isFull()) {
	                // Customer gives up
	                numCustomersWhoTurnedAway++;
                } else {
	                queue.insert(new Customer(customerId, time));
	                customerId++;
                }
            }

            // Then, if in verbose mode, display the state of the simulation
            if (isVerbose) {
                System.out.println("Time: " + time + "\n" + this);
                System.out.println("=====================================================================================================");
            }
        }
    }
    
    public void displayStats() {
        System.out.println("\nSTATISTICS\n");
        System.out.println("Total customers: " + (numCustomersWhoGotServed + numCustomersWhoTurnedAway + queue.length() +
            (sp.isBusy() ? 1 : 0)));
        System.out.println("    Served:         " + numCustomersWhoGotServed);
        System.out.println("    Turned away:    " + numCustomersWhoTurnedAway);
        System.out.println("    Still in queue: " + queue.length());
        System.out.println("Service position idle time: " + servicePositionIdleTime);
        System.out.println("Service position busy time: " + servicePositionBusyTime);
        System.out.println(new BarChart("Waiting Times", waitingFrequencies)); 
    }
    
    public String toString() {
        return sp + "\n" + queue;
    }
   
    private boolean customerHasArrived() { 
        return randy.nextDouble() < probabilityOfArrival;
    }

}
