/*****************************************************************************
 * © 2015 Avaya Inc. All rights reserved.
 ****************************************************************************/
package com.avaya.zephyr.services.sample_services.CallableService;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import javax.mail.MessagingException;
import javax.mail.internet.MimeUtility;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

import com.avaya.collaboration.util.logger.Logger;

@WebServlet("/CallableServiceServlet/*")
@MultipartConfig
public class CallableServiceServlet extends HttpServlet
{
    private static final long serialVersionUID = 1L;
    private transient Logger logger;

    public CallableServiceServlet()
    {
        super();
    }

    @Override
    public void init(ServletConfig config) throws ServletException
    {
        super.init(config);
        this.logger = Logger.getLogger(CallableServiceServlet.class);
        logger.finest("CallableServiceServlet init is called");
    }

    @Override
    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException,
            IOException
    {
        logger.finer("CallableServiceServlet doPost ENTER");
        try
        {
            final File audioFile = getFile(request, response);
            final boolean isDeleted = audioFile.delete();
            if (isDeleted)
            {
                logger.finest("CallableServiceServlet doPost previous record file is removed.");
            }
            try (final FileOutputStream saveAudioFile = new FileOutputStream(audioFile))
            {
                final Part audioPartOfFile = request.getPart("rec_data");
                final InputStream audioInput = audioPartOfFile.getInputStream();
                final byte audioBytes[] = new byte[(int) audioPartOfFile.getSize()];

                while ((audioInput.read(audioBytes)) != -1)
                {
                    InputStream byteAudioStream = new ByteArrayInputStream(decode(audioBytes));
                    final AudioFormat audioFormat = getAudioFormat();
                    AudioInputStream audioInputStream =
                            new AudioInputStream(byteAudioStream, audioFormat, audioBytes.length);

                    if (AudioSystem.isFileTypeSupported(AudioFileFormat.Type.WAVE,
                            audioInputStream))
                    {
                        AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, saveAudioFile);
                    }
                }
                audioInput.close();
                saveAudioFile.flush();
            }
        }
        catch (final Exception e)
        {
            response.setContentType("text/plain");
            PrintWriter pw = response.getWriter();
            pw.write("doPost exception=" + e);
        }
        logger.finer("CallableServiceServlet doPost EXIT");
    }

    @Override
    protected void doPut(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException,
            IOException
    {
        logger.finer("CallableServiceServlet doPut ENTER");
        final File playPrevRecordedFile = getFile(request, response);

        if (playPrevRecordedFile.exists())
        {
            logger.finer("CallableServiceServlet doPut record file exists");
            response.setContentType("text/plain");
            PrintWriter pw = response.getWriter();
            pw.write("EXISTS");
        }
        logger.finer("CallableServiceServlet doPut EXIT");
    }

    @Override
    protected void doDelete(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException,
            IOException
    {
        logger.finer("CallableServiceServlet doDelete ENTER");
        final File fileToDelete = getFile(request, response);
        try
        {
            final boolean isDeleted = fileToDelete.delete();
            if (isDeleted)
            {
                logger.info("CallableServiceServlet doDelete record file is deleted.");
            }
            else
            {
                logger.error("CallableServiceServlet doDelete Could not find the previous recorded file, so it cannot delete it.");
            }
        }
        catch (final Exception e)
        {
            response.setContentType("text/plain");
            PrintWriter pw = response.getWriter();
            pw.write("Cannot delete file: exception=" + e);
        }

        logger.finer("CallableServiceServlet doDelete EXIT");
    }

    /*
     * Avaya recommends that audio played by Avaya Aura MS be encoded as 16-bit,
     * 8 kHz, single channel, PCM files. Codecs other than PCM or using higher
     * sampling rates for higher quality recordings can also be used, however,
     * with reduced system performance. Multiple channels, like stereo, are not
     * supported.
     * 
     * @see Using Web Services on Avaya Aura Media Server Release 7.7, Issue 1,
     * August 2015 on support.avaya.com
     */

    private AudioFormat getAudioFormat()
    {
        final float sampleRate = 8000.0F;
        // 8000,11025,16000,22050,44100
        final int sampleSizeInBits = 16;
        // 8,16
        final int channels = 1;
        // 1,2
        final boolean signed = true;
        // true,false
        final boolean bigEndian = false;
        // true,false
        return new AudioFormat(
                sampleRate,
                sampleSizeInBits,
                channels,
                signed,
                bigEndian);
    }

    public static byte[] decode(byte[] encodedAudioBytes) throws MessagingException, IOException
    {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(encodedAudioBytes);
                InputStream b64InputStream = MimeUtility.decode(byteArrayInputStream, "base64");)
        {
            byte[] tmpAudioBytes = new byte[encodedAudioBytes.length];
            int numberOfBytes = b64InputStream.read(tmpAudioBytes);
            byte[] decodedAudioBytes = new byte[numberOfBytes];
    
            System.arraycopy(tmpAudioBytes, 0, decodedAudioBytes, 0, numberOfBytes);
    
            return decodedAudioBytes;
        }        
    }

    public File getFile(final HttpServletRequest request, final HttpServletResponse response)
    {
        final ServletContext callableServiceServletContext = getServletContext();
        final String contextPath = callableServiceServletContext.getRealPath("/");
        final String filename = request.getPathInfo();
        if (logger.isFinestEnabled())
        {
            logger.finest("CallableServiceServlet getFile filename: " + filename);
        }
        return new File(contextPath, filename);
    }
}
