/*****************************************************************************
 * © 2014 Avaya Inc. All rights reserved.
 ****************************************************************************/
package com.avaya.services.Callingpolicies;

import javax.naming.InitialContext;

import com.avaya.collaboration.businessdata.api.NoAttributeFoundException;
import com.avaya.collaboration.businessdata.api.NoServiceProfileFoundException;
import com.avaya.collaboration.businessdata.api.NoUserFoundException;
import com.avaya.collaboration.businessdata.api.ServiceNotFoundException;

import com.avaya.collaboration.call.Call;
import com.avaya.collaboration.call.Participant;
import com.avaya.collaboration.call.ParticipantFactory;
import com.avaya.collaboration.call.media.DigitOptions;
import com.avaya.collaboration.call.media.MediaFactory;
import com.avaya.collaboration.call.media.MediaServerInclusion;
import com.avaya.collaboration.call.media.MediaService;
import com.avaya.collaboration.call.media.PlayItem;
import com.avaya.collaboration.util.logger.Logger;

public final class PromptAndCollectOperationImpl implements PromptAndCollectOperation
{
    private final ClusterAttributeReader clusterAttributeReader;
    private final TrafficInterfaceAddressRetriever trafficInterfaceAddressRetriever;
    private final ServiceAttributeReader serviceAttributeReader;
    private SequentialForkingTimer noAnswerTimer;
    private final Logger logger;

    private static final String DOMAIN_DELIMITER_AT = "@";
    private static final long NO_ANSWER_TIMEOUT = 10000L;

    public PromptAndCollectOperationImpl()
    {
        this(new ClusterAttributeReaderImpl(),
                new TrafficInterfaceAddressRetrieverImpl(),
                new ServiceAttributeReaderImpl(),
                Logger.getLogger(PromptAndCollectOperationImpl.class));
    }

    PromptAndCollectOperationImpl(final ClusterAttributeReader clusterAttributeReader,
            final TrafficInterfaceAddressRetriever trafficInterfaceAddressRetriever,
            final ServiceAttributeReader serviceAttributeReader,
            final Logger logger)
    {
        this.clusterAttributeReader = clusterAttributeReader;
        this.trafficInterfaceAddressRetriever = trafficInterfaceAddressRetriever;
        this.serviceAttributeReader = serviceAttributeReader;
        this.noAnswerTimer = null;
        this.logger = logger;
    }

    /**
     * Prompt the caller to enter a digit to determine the call behaviour. First, we will use DigitOptions to indicate that we want to collect 1
     * digit. Then, we will use PlayItem to specify the announcement we will use for the prompt.
     */
    @Override
    public void executePromptAndCollectOperation(final Call call)
    {

        logger.finer("executePromptAndCollectOperation ENTER setting MediaServerInclusion.AS_NEEDED");
        // set the Media Inclusion Policy to AS_NEEDED. Media Server is not required for the whole duration of the call.
        call.getCallPolicies().setMediaServerInclusion(MediaServerInclusion.AS_NEEDED);

        CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.NO_SELECTION);

        try
        {
            final InitialContext ctx = new InitialContext();
            final String lookupName = "java:module/SequentialForkingTimer";
            noAnswerTimer = (SequentialForkingTimer) ctx.lookup(lookupName);
        }
        catch (final Exception e)
        {
            logger.finest("executePromptAndCollectOperation - noAnswerTimer error", e);
        }

        final DigitOptions digitOptions = MediaFactory.createDigitOptions();
        digitOptions.setNumberOfDigits(1);
        digitOptions.setTerminationKey("#");
        digitOptions.setTimeout(DigitOptions.DEFAULT_TIMEOUT_MILLISECONDS);

        final PlayItem playItem = MediaFactory.createPlayItem();
        playItem.setInterruptible(true);
        playItem.setIterateCount(1);

        try
        {
            final String announcementFileNetworkProtocol =
                    clusterAttributeReader.getAnnouncementFileNetworkProtocol();
            final String trafficInterfaceAddress =
                    trafficInterfaceAddressRetriever.getTrafficInterfaceAddress();
            final String source =
                    announcementFileNetworkProtocol + "://" + trafficInterfaceAddress +
                            "/services/Callingpolicies/Callingpolicies.wav";
            playItem.setSource(source);
        }
        catch (final Exception e)
        {
            logger.warn("executePromptAndCollectOperation: exception=" + e);
            call.drop();
        }

        if (logger.isFinestEnabled())
        {
            logger.finest("executePromptAndCollectOperation: call=" + call + "; prompt and collect=" +
                    playItem + "; digit options=" + digitOptions);
        }

        final MediaService mediaService = MediaFactory.createMediaService();
        final Participant participant = call.getCallingParty();
        final CallingPoliciesMediaListener callingPoliciesMediaListener =
                new CallingPoliciesMediaListener(call);
        mediaService.promptAndCollect(participant, playItem, digitOptions, callingPoliciesMediaListener);
    }

    /**
     * This method will cancel sequential forking timer.
     */
    @Override
    public void cancelSequentialForkingTimer(final Call call)
    {
        logger.finer("cancelSequentialForkingTimer ENTER");
        if (noAnswerTimer == null)
        {
            logger.finer("cancelSequentialForkingTimer EXIT noAnswerTimer is null");
            return;
        }
        noAnswerTimer.cancelTimer(call.getUCID());
        if (CallingPoliciesMediaListener.SEQUENTIALFORK_DIGIT5.equals(CallingPoliciesMediaListener.callDigitSelectMap.get(call.getId())))
        {
            // clear selected option in order to prevent adding new party in participantDropped() callback for Sequential Forking option
            CallingPoliciesMediaListener.callDigitSelectMap.put(call.getId(), CallingPoliciesMediaListener.NO_SELECTION);
        }
        logger.finer("cancelSequentialForkingTimer EXIT");
    }

    /**
     * This method will set a 10 seconds timer. The code that handles the timeout is here: {@link SequentialForkingTimer}.
     * 
     */
    @Override
    public void startNoAnswerTimer(final Call call)
    {
        logger.finer("startNoAnswerTimer ENTER");
        final String callId = call.getUCID();
        if (noAnswerTimer == null)
        {
            logger.finer("startNoAnswerTimer EXIT noAnswerTimer is null");
            return;
        }
        noAnswerTimer.createTimer(NO_ANSWER_TIMEOUT, callId);
        logger.finer("startNoAnswerTimer EXIT");
    }

    /**
     * This method will add new party (redirect number snap-in attribute) to a call.
     */
    @Override
    public void addPartyToCall(final Call call)
    {
        logger.finer("addPartyToCall ENTER");
        try
        {
            call.addParticipant(getPartyToAdd(serviceAttributeReader.getRedirectNumber(call.getCalledParty())));
        }
        catch (final Exception e)
        {
            logger.error("addPartyToCall: failed operation exception=", e);
            call.drop();
        }
        logger.finer("addPartyToCall EXIT");
    }

    private Participant getPartyToAdd(final String partyToAddNumber) throws NoUserFoundException,
            NoAttributeFoundException, ServiceNotFoundException, NoServiceProfileFoundException
    {
        logger.finer("getPartyToAdd ENTER");
        final Participant partyToAdd;
        if (partyToAddNumber.contains(DOMAIN_DELIMITER_AT))
        {
            final String handle = getHandle(partyToAddNumber);
            final String domain = getDomain(partyToAddNumber);
            final String displayNumber = "Adding New Party ..." + handle;
            partyToAdd = ParticipantFactory.create(handle, domain, displayNumber);
        }
        else
        {
            partyToAdd = ParticipantFactory.create(partyToAddNumber,
                    clusterAttributeReader.getDefaultClusterDomain());
        }
        if (logger.isFinerEnabled())
        {
            logger.finer("getPartyToAdd " + partyToAdd);
        }
        return partyToAdd;
    }

    private String getHandle(final String forkedParticipant)
    {
        final int delimiterIndex = forkedParticipant.indexOf(DOMAIN_DELIMITER_AT);
        final String handle = forkedParticipant.substring(0, delimiterIndex);

        return handle;
    }

    private String getDomain(final String forkedParticipant)
    {
        final int delimeterIndex = forkedParticipant.indexOf(DOMAIN_DELIMITER_AT);
        final String domain = forkedParticipant.substring(delimeterIndex + 1, forkedParticipant.length());

        return domain;
    }
}
