package SimkitOpenDis7Examples;

import simkit.Priority;
import simkit.SimEntityBase;
import simkit.random.RandomVariate;

/**
 * Simple Server component. Instances of this class cannot be used on their own,
 * but require another SimEntity to schedule Arrival events. If it is desired to
 * listen to an event of another name,use an Adapter instance.
 *
 * The StartService is schedule with a positive priority, which will ensure it
 * will occur ahead of any simultaneously scheduled Arrival events, as long as
 * they have the default priority of 0.0.
 *
 * @author abuss@nps.edu
 */
public class SimpleServer extends SimEntityBase {

    /**
     * Total number of servers
     */
    private int totalNumberServers;
    
    /**
     * Generates service times
     */
    private RandomVariate serviceTimeGenerator;

    /**
     * number of available servers at any time
     */
    protected int numberAvailableServers;
    
    /**
     * number in queue at any time
     */
    protected int numberInQueue;
    
    /**
     * Number customers served
     */
    protected int numberServed;

    /**
     * Zero-argument constructor
     */
    public SimpleServer() {
    }

    /**
     * Creates a new instance of SimpleServer with the given parameters
     *
     * @param totalNumberServers Total number of servers
     * @param serviceTimeGenerator Service time generator. Must be RandomVariate
     * instance that only generates non-negative values.
     */
    public SimpleServer(int totalNumberServers, RandomVariate serviceTimeGenerator) {
        setTotalNumberServers(totalNumberServers);
        setServiceTimeGenerator(serviceTimeGenerator);
    }

    /**
     * Set numberAvailable servers to total number servers, numberInQueue to 0,
     * numberServed to 0.
     */
    @Override
    public void reset() {
        super.reset();
        numberInQueue = 0;
        numberAvailableServers = totalNumberServers;
        numberServed = 0;
    }

    /**
     * Just fires PropertyChange events
     */
    public void doRun() {
        firePropertyChange("numberInQueue", getNumberInQueue());
        firePropertyChange("numberAvailableServers", getNumberAvailableServers());
        firePropertyChange("numberServed", getNumberServed());
    }

    /**
     * Increment number in queue. If a server is available, schedule
     * StartService immediately with priority of 1.0
     */
    public void doArrival() {
        int oldNumberInQueue = numberInQueue;
        numberInQueue = numberInQueue + 1;
        firePropertyChange("numberInQueue", oldNumberInQueue, getNumberInQueue());
        if (getNumberAvailableServers() > 0) {
            waitDelay("StartService", 0.0, Priority.HIGH);
        }
    }

    /**
     * Decrement numberInQueue and numberAvailableServers Schedule EndService
     * after service time delay
     */
    public void doStartService() {
        int oldNumberInQueue = numberInQueue;
        numberInQueue = numberInQueue - 1;
        firePropertyChange("numberInQueue", oldNumberInQueue, numberInQueue);
        int oldNumberAvailableServers = numberAvailableServers;
        numberAvailableServers = numberAvailableServers - 1;
        firePropertyChange("numberAvailableServers", oldNumberAvailableServers, numberAvailableServers);

//        double serviceTime = getServiceTimeGenerator().generate();
//        firePropertyChange("serviceTime", serviceTime);

        waitDelay("EndService", serviceTimeGenerator);

    }

    /**
     * Increment numberAvailableServers If customers in queue, schedule
     * StartService immediately with HIGH priority
     */
    public void doEndService() {
        int oldNumberAvailableServers = numberAvailableServers;
        numberAvailableServers = numberAvailableServers + 1;
        firePropertyChange("numberAvailableServers", oldNumberAvailableServers, numberAvailableServers);

        int oldNumberServed = numberServed;
        numberServed = numberServed + 1;
        firePropertyChange("numberServed", oldNumberServed, numberServed);

        if (getNumberInQueue() > 0) {
            waitDelay("StartService", 0.0, Priority.HIGH);
        }
    }

    /**
     * accessor method to get a state variable
     * @return the numberAvailableServers
     */
    public int getNumberAvailableServers() {
        return numberAvailableServers;
    }

    /**
     * accessor method to get a state variable
     * @return the numberInQueue
     */
    public int getNumberInQueue() {
        return numberInQueue;
    }

    /**
     * accessor method to set a state variable
     * @param totalNumberServers total number of servers
     * @throws IllegalArgumentException if totalNumberServers < 0
     */
    public void setTotalNumberServers(int totalNumberServers) {
        if (totalNumberServers <= 0) {
            throw new IllegalArgumentException("Need positive number of servers: " + totalNumberServers);
        }
        this.totalNumberServers = totalNumberServers;
    }

    /**
     * accessor method to get a state variable
     * @return the serviceTimeGenerator
     */
    public RandomVariate getServiceTimeGenerator() {
        return this.serviceTimeGenerator;
    }

    /**
     * accessor method to set a state variable
     * @param serviceTimeGenerator the serviceTimeGenerator to set
     */
    public void setServiceTimeGenerator(RandomVariate serviceTimeGenerator) {
        this.serviceTimeGenerator = serviceTimeGenerator;
    }

    /**
     * accessor method to get a state variable
     * @return the totalNumberServers
     */
    public int getTotalNumberServers() {
        return this.totalNumberServers;
    }

    /**
     * accessor method to get a state variable
     * @return the numberServed
     */
    public int getNumberServed() {
        return this.numberServed;
    }
}