package org.jivesoftware.xmpp.workgroup.spi.dispatcher;

import org.jivesoftware.xmpp.workgroup.AgentSession;
import org.jivesoftware.xmpp.workgroup.Offer;
import org.jivesoftware.xmpp.workgroup.Workgroup;
import org.jivesoftware.xmpp.workgroup.dispatcher.AgentSelector;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Basic implementation that uses the following condition for selecting possible agents:
 * <ul>
 * <li>the presence status of the agent allows to receive offers</li>
 * <li>the maximum of chats has not been reached for the agent</li>
 * <li>the agent has not rejected the offer before</li>
 * <li>the agent does not have to answer a previuos offer</li>
 * </ul>
 * <p/>
 * And the best agent will be the one with less number of chats. If more than one agent has
 * the less number of chats then the agent that never rejected the offer will be selected.
 *
 * @author Gaston Dombiak
 */
public class BasicAgentSelector implements AgentSelector {

    /**
     * Returns true if the agent session may receive an offer. An agent session may receive new
     * offers if:
     * <p/>
     * 1) the presence status of the agent allows to receive offers
     * 2) the maximum of chats has not been reached for the agent
     * 3) the agent has not rejected the offer before
     * 4) the agent does not have to answer a previuos offer
     */
    public boolean validateAgent(AgentSession session, Offer offer) {
        Workgroup workgroup = offer.getRequest().getWorkgroup();
        return session != null && session.isAvailableToChat() &&
                session.getCurrentChats(workgroup) < session.getMaxChats(workgroup) && !offer.isRejector(session) &&
                !session.isWaitingOfferAnswer();
    }

    /**
     * The best agent will be the agent with less number of chats. If more than one agent has
     * the less number of chats then select the agent that never rejected the offer.
     */
    public AgentSession bestAgentFrom(List<AgentSession> possibleSessions, Offer offer) {
        Collections.sort(possibleSessions, new SessionComparator(offer));
        int size = possibleSessions.size() - 1;
        return possibleSessions.get(size);
    }

    /**
     * Sort/compare the AgentSessions based on the number of current chats. But if the number of
     * chats is equal then sort/compare based on previous rejections of the offer.
     */
    private class SessionComparator implements Comparator<AgentSession> {

        private Offer offer;

        public SessionComparator(Offer offer) {
            super();
            this.offer = offer;
        }

        public int compare(AgentSession item1, AgentSession item2) {
            Workgroup workgroup = offer.getRequest().getWorkgroup();
            int int1 = item1.getCurrentChats(workgroup);
            int int2 = item2.getCurrentChats(workgroup);

            if (int1 == int2) {
                // Base the decision on previous offer rejections
                int rejec1 = offer.getRejections().contains(item1.getJID().toBareJID()) ? 1 : 0;
                int rejec2 = offer.getRejections().contains(item2.getJID().toBareJID()) ? 1 : 0;

                if (rejec1 == rejec2) {
                    long time1 = item1.getTimeLastChatEnded() == null ? 0 : item1.getTimeLastChatEnded().getTime();
                    long time2 = item2.getTimeLastChatEnded() == null ? 0 : item2.getTimeLastChatEnded().getTime();

                    if (time1 == time2) {
                        return 0;
                    }

                    if (time1 > time2) {
                        return -1;
                    }

                    if (time1 < time2) {
                        return 1;
                    }

                }
                else if (rejec1 > rejec2) {
                    return -1;
                }
                else {
                    return 1;
                }
            }

            if (int1 > int2) {
                return -1;
            }

            if (int1 < int2) {
                return 1;
            }

            return 0;
        }
    }


}
