package org.atea.nlptools.macroniser.servlets;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import com.google.gson.Gson;
import com.google.gson.stream.JsonWriter;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.atea.nlptools.macroniser.monogram.restorer.MonogramRestorer;
import org.atea.nlptools.macroniser.monogram.restorer.RestoredWord;
import org.atea.nlptools.macroniser.util.StringUtil;

/**
 * Represents a servlet for macronising direct text input.
 * @author Carl Stephens
 * @version 2.0
 */
@MultipartConfig
public class DirectServlet extends HttpServlet
{
    private static final Logger logger = LogManager.getLogger(DirectServlet.class);
    
    private final Gson gsonInstance = new Gson();

    @Override
    public void init(ServletConfig config) throws ServletException
    {
        super.init(config);
    }

    /**
     * Handles the HTTP <code>GET</code> method.
     * @param request The servlet request.
     * @param response The servlet response.
     * @throws IllegalStateException if a reponse has already been committed.
     * @throws IOException if an I/O error occurs.
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws IllegalStateException, IOException
    {
        response.sendError(HttpStatusCode.MethodNotAllowed, "Expected a POST request with multipart form data.");
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     * @param request The servlet request.
     * @param response The servlet response.
     * @throws IllegalStateException if a response has already been committed.
     * @throws IOException if an I/O error occurs.
     * @throws ServletException if a servlet-specific error occurs.
     * @throws UnsupportedEncodingException if the selected encoding is not supported.
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws IllegalStateException, IOException, ServletException, UnsupportedEncodingException
    {
        try
        {
            final Boolean shouldPreserveMacrons = Boolean.parseBoolean(request.getParameter("preserveExistingMacrons"));

            Part fragmentPart = null;
            try
            {
                fragmentPart = request.getPart("fragment");
            }
            catch (IllegalStateException ise)
            {
                response.sendError(HttpStatusCode.PayloadTooLarge);
                return;
            }
            catch (ServletException se)
            {
                response.sendError(HttpStatusCode.BadRequest, "Expected multipart form data.");
                return;
            }

            if (fragmentPart == null)
            {
                response.sendError(HttpStatusCode.BadRequest, "The 'fragment' parameter must be supplied");
                return;
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(fragmentPart.getInputStream()));

            final MonogramRestorer restorer = new MonogramRestorer(shouldPreserveMacrons);
            final Pattern pattern = Pattern.compile("[^\\s]+");

            response.setContentType("application/json; charset=UTF-8");
            final JsonWriter writer = gsonInstance.newJsonWriter(response.getWriter());
            writer.beginArray();

            String line = null;
            int linebreakCount = -1;

            while ((line = reader.readLine()) != null)
            {
                linebreakCount++;
                if (!StringUtil.isNullOrWhiteSpace(line) && linebreakCount > 0)
                {
                    writer.beginObject();
                    writer.name("linebreaks");
                    writer.value(linebreakCount);
                    writer.endObject();
                    
                    linebreakCount = 0;
                }

                Matcher matcher = pattern.matcher(line);
                while (matcher.find())
                {
                    String token = matcher.group();

                    writer.beginObject();

                    RestoredWord restoredWord = restorer.restore(token);

                    writer.name("w");
                    writer.value(restoredWord.getWord());

                    if (restoredWord.getWasMacronised())
                    {
                        writer.name("macronised");
                        writer.value(true);
                    }

                    writer.endObject();
                }
            }
            
            writer.endArray();
            writer.flush();
        }
        catch (Exception ex)
        {
            logger.error("Failed to restore macrons", ex);

            response.sendError(
                HttpStatusCode.InternalServerError,
                "An unexpected error has occurred. Please try again or contact the web administrator if the problem persists."
            );
        }
    }
}
