///////////////////////////////////////////////////////////////////////////////
//// MultiChanBroadcastAnnouncementSender.java
////Description::MultiChanBroadcastAnnouncementSender makes a call and plays an announcement for list of Numbers in the request
////
//// Copyright 2013 Avaya Inc. All rights reserved.
//// Usage of this source is bound to the terms described
//// in http://www.avaya.com/devconnect
//// Avaya - Confidential & Proprietary. Use pursuant to your signed agreement or Avaya Policy.
/////////////////////////////////////////////////////////////////////////////////

package com.avaya.zephyr.services.multichanbroadcast.announcementcall;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.avaya.collaboration.businessdata.api.ServiceData;
import com.avaya.collaboration.call.Call;
import com.avaya.collaboration.call.CallFactory;
import com.avaya.collaboration.call.Participant;
import com.avaya.collaboration.call.ParticipantFactory;
import com.avaya.collaboration.util.logger.Logger;

public class MultiChanBroadcastAnnouncementSender
{
    private final Logger logger;
    private ServiceData serviceData = null;
    private HttpServletRequest httpRequest = null;
    private PrintWriter printWriter = null;
    private static final String ANNOUNCEMENT_PARAM_HTTP_FILE_URI = "announcementFileUri";
    private static final String ANNOUNCEMENT_INFO_NO_FILE_URI_LOG =
            "Info: No announcement specified in the HTTP request.";
    private static final String ANNOUNCEMENT_PARAM_HTTP_TO = "announcementTo";
    private static final String ANNOUNCEMENT_INFO_NO_TO_LOG =
            "Info: No announcement recipients specified in HTTP request.";
    private static final String ANNOUNCEMENT_PARAM_SMGR_CALLING_NUMBER = "announcementCallingNumber";
    private static final String ANNOUNCEMENT_INFO_NO_CALLING_NUMBER_LOG =
            "Info: No calling handle defined in the MultiChanBroadcastService attribute page on SMGR.";
    private static final String ANNOUNCEMENT_ERROR_CALLING_NUMBER_LOG =
            "Error: Error occured acquiring 'announcementCallingNumber'.";
    private static final String ANNOUNCEMENT_PARAM_SMGR_CALLING_DOMAIN = "announcementCallingDomian";
    private static final String ANNOUNCEMENT_INFO_NO_CALLING_DOMAIN_LOG =
            "Info: No calling domain defined in the MultiChanBroadcastService attribute page on SMGR.";
    private static final String ANNOUNCEMENT_ERROR_CALLING_DOMAIN_LOG =
            "Error: Error occured acquiring 'announcementCallingDomian'.";
    private static final String ANNOUNCEMENT_PARAM_SMGR_CALLING_DISPLAY = "announcementCallingName";
    private static final String ANNOUNCEMENT_ERROR_CALLING_DISPLAY_LOG =
            "Error: Error occured acquiring 'announcementCallingName'.";
    private static final String ANNOUNCEMENT_ERROR_PROMPTANDCOLLECT_TIMEOUT_LOG =
            "Error: Error occured acquiring 'announcementPromptAndCollectTimeout'. Defaulting to 90 seconds.";
    private static final String ANNOUNCEMENT_PARAM_SMGR_PROMPTANDCOLLECT_TIMEOUT =
            "announcementPromptAndCollectTimeout";
    private static final String ANNOUNCEMENT_PARAM_CALL_FILE_URIS = ANNOUNCEMENT_PARAM_HTTP_FILE_URI + "s";
    private static final String ANNOUNCEMENT_PARAM_CALL_PROMPTANDCOLLECT_TIMEOUT =
            "announcementPromptAndCollectTimeout";
    private static final String ANNOUNCEMENT_INFO_SENT_LOG = "Info: Announcement queued to be sent to ";
    private static final String ANNOUNCEMENT_INFO_NOT_SENT_LOG = "Info: Announcements will not be sent.";
    private static final String ANNOUNCEMENT_ERROR_SEND_LOG = "Error: Error occured sending announcement to ";
    private static final int ANNOUNCEMENT_DEFAULT = 90;
    private static final int ANNOUNCEMENT_MAXIMUM = 300;
    private static final int MS_PER_SEC = 1000;

    public MultiChanBroadcastAnnouncementSender(final ServiceData serviceData,
            final HttpServletRequest httpRequest, final PrintWriter printWriter)
    {
        this.serviceData = serviceData;
        this.httpRequest = httpRequest;
        this.printWriter = printWriter;
        logger = Logger.getLogger(MultiChanBroadcastAnnouncementSender.class);
    }

    // Junit constructor
    public MultiChanBroadcastAnnouncementSender(final ServiceData serviceData,
            final HttpServletRequest httpRequest, final PrintWriter printWriter, final Logger logger)
    {
        this.serviceData = serviceData;
        this.httpRequest = httpRequest;
        this.printWriter = printWriter;
        this.logger = logger;
    }


    // Get the list of Media File URI's from the http request which would be used for playing Announcement
    private List<String> getAnnouncementFileUris()
    {
        // Required - From HTTP request
        final String[] announcementFileUrisFromHttpRequest =
                httpRequest.getParameterValues(ANNOUNCEMENT_PARAM_HTTP_FILE_URI);

        // Dynamic list to build collection of announcementTos.
        final List<String> announcementFileUris = new ArrayList<String>();

        // Determine if the key/value pairs had at least one actual values.
        if (announcementFileUrisFromHttpRequest != null)
        {
            for (final String announcementFileUri : announcementFileUrisFromHttpRequest)
            {
                if (!announcementFileUri.isEmpty())
                {
                    announcementFileUris.add(announcementFileUri);
                }
            }
        }

        if (announcementFileUris.isEmpty())
        {
            logHttpResponseInfo(ANNOUNCEMENT_INFO_NO_FILE_URI_LOG);
        }

        return announcementFileUris;
    }

    //Get the list of numbers from http request to which Announcement would be played
    private List<String> getAnnouncementTos()
    {
        // Required - From HTTP request
        final String[] announcementTosFromHttpRequest =
                httpRequest.getParameterValues(ANNOUNCEMENT_PARAM_HTTP_TO);

        // Dynamic list to build collection of announcementTos.
        final List<String> announcementTos = new ArrayList<String>();

        // Convert the list of announcementTo (digit string or URI)
        // into a list of strings and separate if delimited by
        // commas
        //
        // HTTP Request contains...
        // announcementTo = "5555"
        // announcementTo = "5556"
        // announcementTo = "5557@domain.com"
        // announcementTo = "5558@domain.com"
        // vs.
        // announcementTo = "5555, 5556"
        // announcementTo = "5557@domain.com, 5558@domain.com"
        // vs.
        // announcementTo = "5555, 5556, 5557@domain.com, 5558@domain.com"
        if (announcementTosFromHttpRequest != null)
        {
            for (final String announcementTosCommaDelimited : announcementTosFromHttpRequest)
            {
                // Separate entries if delimited with a comma
                for (String announcementTo : announcementTosCommaDelimited.split(","))
                {
                    if (announcementTo.isEmpty())
                    {
                        continue;
                    }
                    announcementTos.add(announcementTo);
                }
            }
        }

        if (announcementTos.isEmpty())
        {
            logHttpResponseInfo(ANNOUNCEMENT_INFO_NO_TO_LOG);
        }

        return announcementTos;
    }

    // used for displaying the calling number when originating announcement
    // calls.
    protected String getAnnouncementCallingHandle()
    {
        // Required - From SMGR GUI
        String announcementCallingHandle = "";

        try
        {
            announcementCallingHandle =
                    serviceData.getServiceAttribute(ANNOUNCEMENT_PARAM_SMGR_CALLING_NUMBER);
            if (announcementCallingHandle.isEmpty())
            {
                logHttpResponseInfo(ANNOUNCEMENT_INFO_NO_CALLING_NUMBER_LOG);
            }
        }
        catch (final Exception e)
        {
            logHttpResponseError(ANNOUNCEMENT_ERROR_CALLING_NUMBER_LOG, e);
        }

        return announcementCallingHandle;
    }


    // Used for displaying the calling domain when originating announcement
    // calls, read from SMGR GUI.
    private String getAnnouncementCallingDomain()
    {
        // Required - From SMGR GUI
        String announcementCallingDomain = "";

        try
        {
            announcementCallingDomain =
                    serviceData.getServiceAttribute(ANNOUNCEMENT_PARAM_SMGR_CALLING_DOMAIN);
            if (announcementCallingDomain.isEmpty())
            {
                logHttpResponseInfo(ANNOUNCEMENT_INFO_NO_CALLING_DOMAIN_LOG);
            }
        }
        catch (final Exception e)
        {
            logHttpResponseError(ANNOUNCEMENT_ERROR_CALLING_DOMAIN_LOG, e);
        }

        return announcementCallingDomain;
    }



    // Used for displaying the calling name when originating announcement calls,
    // read from SMGR GUI.
    private String getAnnouncementCallingDisplay()
    {
        // NOT Required - From SMGR GUI
        String announcementCallingDisplay = "";

        try
        {
            announcementCallingDisplay =
                    serviceData.getServiceAttribute(ANNOUNCEMENT_PARAM_SMGR_CALLING_DISPLAY);
        }
        catch (final Exception e)
        {
            logHttpResponseError(ANNOUNCEMENT_ERROR_CALLING_DISPLAY_LOG, e);
        }

        return announcementCallingDisplay;
    }



    // Returns a timeout for Collecting the DTMF digits
    private Integer getPromptAndCollectTimeout()
    {
        // NOT Required - From SMGR GUI
        Integer promptAndCollectTimeout = ANNOUNCEMENT_DEFAULT;

        try
        {
            promptAndCollectTimeout =
                    Integer.parseInt(serviceData
                            .getServiceAttribute(ANNOUNCEMENT_PARAM_SMGR_PROMPTANDCOLLECT_TIMEOUT));
            if ((promptAndCollectTimeout <= 0) || (promptAndCollectTimeout > ANNOUNCEMENT_MAXIMUM))
            {
                promptAndCollectTimeout = ANNOUNCEMENT_MAXIMUM;
            }
        }
        catch (final Exception e)
        {
            logHttpResponseError(ANNOUNCEMENT_ERROR_PROMPTANDCOLLECT_TIMEOUT_LOG, e);
        }

        return promptAndCollectTimeout * MS_PER_SEC;
    }

    private void logHttpResponseInfo(final String log)
    {
        printWriter.println(log);
        logger.info(log);
    }


    private void logHttpResponseError(final String log, final Exception e)
    {
        printWriter.println(log);
        if (e != null)
        {
            logger.error(log, e);
        }
        else
        {
            logger.error(log);
        }
    }


    // initiates a call and attaches a call listener which plays an announcement to the list of recipients on Call Answer.
    public boolean sendAnnouncement()
    {
        boolean atLeastOneAnnouncementSent = false;

        final List<String> announcementFileUris = getAnnouncementFileUris();
        final List<String> announcementTos = getAnnouncementTos();

        // Verify necessary members are populated needed to send announcement
        if (announcementFileUris.isEmpty() || announcementTos.isEmpty())
        {
            logHttpResponseInfo(ANNOUNCEMENT_INFO_NOT_SENT_LOG);
        }
        else
        {
            final String announcementCallingHandle = getAnnouncementCallingHandle();
            final String announcementCallingDomain = getAnnouncementCallingDomain();
            final String announcementCallingDisplay = getAnnouncementCallingDisplay();
            final Integer promptAndCollectTimeout = getPromptAndCollectTimeout();

            if (announcementCallingHandle.isEmpty() || announcementCallingDomain.isEmpty())
            {
                logHttpResponseInfo(ANNOUNCEMENT_INFO_NOT_SENT_LOG);
            }
            else
            {
                // Create one calling participant and one call listener for
                // making all the outgoing calls
                final Participant ceParticipant =
                        ParticipantFactory.create(announcementCallingHandle, announcementCallingDomain,
                                announcementCallingDisplay);

                for (final String announcementTo : announcementTos)
                {
                    final Call call = CallFactory.create(ceParticipant, announcementTo);
                    
                    try
                    {
                        // Create, populate, and initiate the call
                        call.setAttribute(ANNOUNCEMENT_PARAM_CALL_FILE_URIS,
                              (ArrayList<String>)announcementFileUris);
                        call.setAttribute(ANNOUNCEMENT_PARAM_CALL_PROMPTANDCOLLECT_TIMEOUT,
                                promptAndCollectTimeout);
                        call.initiate();
                        logHttpResponseInfo(ANNOUNCEMENT_INFO_SENT_LOG + announcementTo);
                        atLeastOneAnnouncementSent = true;
                    }
                    catch (final Exception e)
                    {
                        logHttpResponseError(ANNOUNCEMENT_ERROR_SEND_LOG + announcementTo, e);
                        // drop the call in order to clean up the resources on Breeze 
                        call.drop();
                    }
                }
            }
        }

        return atLeastOneAnnouncementSent;
    }
}
