/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package com.queplix.core.modules.eqlext.www;

import com.queplix.core.error.IncorrectParameterException;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.integrator.security.SecurityException;
import com.queplix.core.integrator.security.WebLoginManager;
import com.queplix.core.modules.eqlext.error.FileManagerException;
import com.queplix.core.modules.eqlext.utils.FileManager;
import com.queplix.core.modules.eqlext.utils.images.FileUploadManager;
import com.queplix.core.utils.StringHelper;
import com.queplix.core.utils.www.AbstractServlet;
import com.queplix.core.utils.www.ServletHelper;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;


/**
 * Servlet for the file upload operations.
 * The following actions are available:
 * <p/>
 * <p/>
 * Get the previously uploaded file by ID.
 * <br>
 * Usage: <pre>/fileUpload/get?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>id</b> - unique file ID,</li>
 * <li><b>filepath</b> - relative file path (subfolder + filename),</li>
 * <li><b>save</b> - save returned file to disk on client side, instead of opening it.</li>
 * </ul>
 * <br>
 * <p/>
 * or:
 * <br>
 * <p/>
 * <pre>/graphics/<filepath></pre>
 * <p/>
 * <br>
 * Examples:
 * <pre>
 *   GET /fileUpload/get?id=123
 *   GET /fileUpload/get?filepath=dd/image.gif
 *   GET /fileUpload/get?filepath=dd/image.gif&save=true
 *   GET /graphics/dd/image.gif
 * </pre>
 * </p>
 * <p/>
 * <p/>
 * Create (upload) a new file.
 * <br>
 * Usage: <pre>/fileUpload/new?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>filename</b> - file name,</li>
 * <li><b>subfolder</b> - subfolder under the root upload dir,</li>
 * <li><b>entity</b> - entity to bind the file with,</li>
 * <li><b>recordID</b> - unique entity record ID.</li>
 * </ul>
 * <br>
 * Example:
 * <pre>
 *   POST /fileUpload/new?filename=image.gif&subfolder=dd&entity=employee&recordId=456
 * </pre>
 * </p>
 * <p/>
 * <p/>
 * Update the existing file.
 * <br>
 * Usage: <pre>/fileUpload/update?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>id</b> - unique file ID.</li>
 * </ul>
 * </p>
 * <p/>
 * <p/>
 * Delete the existing file.
 * <br>
 * Usage: <pre>/fileUpload/delete?...</pre>
 * <br>
 * Parameters:
 * <ul>
 * <li><b>id</b> - unique file ID.</li>
 * </ul>
 * </p>
 * <p/>
 * <p/>
 * Synchronize the file storage (AKA upload dir).
 * <br>
 * Usage: <pre>/fileUpload/sync</pre>
 * </p>
 * <p/>
 * <p/>
 * Synchronize the files.
 * <br>
 * Usage: <pre>/fileUpload/syncfiles</pre>
 * </p>
 *
 * @author [ALB] Baranov Andrey
 * @author [DBR] Daniel B. Raskin
 * @author [ONZ] Oleg N. Zhovtanyuk
 * @version $Revision: 1.2 $ $Date: 2006/01/24 20:57:19 $
 */

public class FileUploadServlet
        extends AbstractServlet {

    // ==================================================== Constants and fields

    // Init parameter names.
    private static final String MAX_UPLOAD_SIZE = "maxUploadSize";


    // Init parameters.
    private int maxUploadSize;

    // ===================================================== Servlet API methods

    /* (non-Javadoc)
     * @see GenericServlet#init(ServletConfig)
     */

    public void init(ServletConfig conf)
            throws ServletException {

        super.init(conf);

        this.maxUploadSize = Integer.parseInt(conf.getInitParameter(
                MAX_UPLOAD_SIZE));

        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG(
                    "File upload servlet initialized: max. upload size = " +
                            maxUploadSize);
        }
    }


    /* (non-Javadoc)
     * @see HttpServlet#service(HttpServletRequest, HttpServletResponse)
     */
    public void service(HttpServletRequest request,
                        HttpServletResponse response)
            throws IOException, ServletException {

        // Set response headers.
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");

        // Get action.
        String action = request.getPathInfo();
        if(action != null) {
            action = action.substring(1);
        } else {
            action = StringHelper.EMPTY_VALUE;
        }

        // Action switch.
        try {
            if(action.equalsIgnoreCase("get")) {
                doGetAction(request, response);
            } else if(action.equalsIgnoreCase("getAttach")) {
                doGetAttachmentAction(request, response);
            } else if(action.equalsIgnoreCase("new")) {
                doNewAction(request, response);
            } else if(action.equalsIgnoreCase("attach")) {
                doAttachAction(request, response);
            } else if(action.equalsIgnoreCase("update")) {
                doUpdateAction(request, response);
            } else if(action.equalsIgnoreCase("delete")) {
                doDeleteAction(request, response);
            } else if(action.equalsIgnoreCase("sync")) {
                doSyncAction(request, response);
            } else if(action.equalsIgnoreCase("syncfiles")) {
                doSyncFilesAction(request, response);
            } else {
                doGetAction(action, response);
            }
        } catch (SecurityException ex) {
            throw new ServletException(ex);
        }
    }

    // ========================================================== Action handler

    //
    // Downloads the file by name.
    //

    protected void doGetAction(String filepath, HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        try {
            // Load file.
            FileManager.FileInfo fi = FileManager.loadFile(filepath);

            // Print file out.
            outFile(fi, false, response);

        } catch (FileManagerException ex) {
            Throwable t = ex.returnCause();
            if(t != null && (t instanceof FileNotFoundException)) {
                // File not found!
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            } else {
                throw new ServletException(ex);
            }
        }
    }


    //
    // Downloads the file by ID.
    //
    protected void doGetAction(HttpServletRequest request,
                               HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);

        String id = ServletHelper.getParamAsString(request, "id", false);
        String filepath = ServletHelper.getParamAsString(request, "filepath",
                false);
        boolean saveOnClientSide = ServletHelper.getParamAsBoolean(request,
                "save");

        try {
            // Load file.
            FileManager.FileInfo fi;
            if(!StringHelper.isEmpty(id)) {
                fi = manager.loadFile(Long.parseLong(id));
            } else {
                fi = FileManager.loadFile(filepath);
            }

            // Print file out.
            outFile(fi, saveOnClientSide, response);

        } catch (FileManagerException ex) {
            Throwable t = ex.returnCause();
            if(t != null && (t instanceof FileNotFoundException)) {
                // File not found!
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            } else {
                throw new ServletException(ex);
            }
        }
    }

    //
    // Downloads attachment by ID.
    //
    protected void doGetAttachmentAction(HttpServletRequest request,
                                         HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);

        String id = ServletHelper.getParamAsString(request, "id", false);

        if(StringHelper.isEmpty(id)) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            return;
        } else {
            try {
                // Load file.
                byte[] data = manager.loadFileAsByteArray(Long.parseLong(id));
                // Print file out.
                outFile(data, response);

            } catch (FileManagerException ex) {
                Throwable t = ex.returnCause();
                if(t != null && (t instanceof FileNotFoundException)) {
                    // File not found!
                    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                } else {
                    throw new ServletException(ex);
                }
            }
        }
    }

    //
    // Uploads a new file by ID.
    //
    protected void doNewAction(HttpServletRequest request,
                               HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);
        List items = getPostItems(request);

        // Parse HTTP request.
        String fileName = null;
        String subFolder = null;
        String entityName = null;
        Long recordID = null;
        FileItem fileItem = null;
        boolean isInlineMemo = false;

        for(Iterator it = items.iterator(); it.hasNext();) {
            FileItem item = (FileItem) it.next();
            if(!item.isFormField()) {
                fileItem = item;
            } else if(item.getFieldName().equals("filename")) {
                fileName = item.getString();
            } else if(item.getFieldName().equals("subfolder")) {
                subFolder = item.getString();
            } else if(item.getFieldName().equals("entity")) {
                entityName = item.getString();
            } else if(item.getFieldName().equals("recordId")) {
                recordID = new Long(item.getString());
            } else if(item.getFieldName().equals("inlineMemo")) {
                isInlineMemo = true;
            }
        }

        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG("File data:" +
                    "\n\t file name = '" + fileName + "'" +
                    "\n\t folder = '" + subFolder + "'" +
                    "\n\t entity = '" + entityName + "'" +
                    "\n\t record ID = " + recordID);
        }

        // Check for the required parameters.
        if(fileItem == null) {
            throw new IncorrectParameterException("file");
        }

        // Create a new file and get its URL.
        File file = manager.saveFile(fileItem, subFolder, entityName, recordID);

        // Get file path and name.
        String filePath = manager.getLocalName(file);
        if(StringHelper.isEmpty(fileName)) {
            fileName = filePath;
        }

        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG("Inserted file: " + filePath);
        }

        // Send HTML to close the window on client.
        response.setContentType(ServletHelper.CONTENT_TYPE_TEXT);
        PrintWriter out = response.getWriter();
        out.println("|" + filePath + "|RESULT=SUCCESS|");
    }


    //
    // Uploads a new file by ID.
    //
    protected void doAttachAction(HttpServletRequest request,
                                  HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);
        List items = getPostItems(request);

        // Parse HTTP request.
        String fileName = null;
        String subFolder = null;
        FileItem fileItem = null;
        boolean isInlineMemo = false;

        for(Iterator it = items.iterator(); it.hasNext();) {
            FileItem item = (FileItem) it.next();
            if(!item.isFormField()) {
                fileItem = item;
            } else if(item.getFieldName().equals("filename")) {
                fileName = item.getString();
            } else if(item.getFieldName().equals("subfolder")) {
                subFolder = item.getString();
            } else if(item.getFieldName().equals("inlineMemo")) {
                isInlineMemo = true;
            }
        }

        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG("File data:" +
                    "\n\t file name = '" + fileName + "'" +
                    "\n\t folder = '" + subFolder + "'");
        }

        // Check for the required parameters.
        if(fileItem == null) {
            throw new IncorrectParameterException("file");
        }

        // Create a new file and get its URL.
        File file = manager.saveFile(fileItem, subFolder);

        // Get file path and name.
        String filePath = manager.getLocalName(file);
        if(StringHelper.isEmpty(fileName)) {
            fileName = filePath;
        }

        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG("Inserted file: " + filePath);
        }

        // Send HTML to close the window on client.
        response.setContentType(ServletHelper.CONTENT_TYPE_TEXT);
        PrintWriter out = response.getWriter();
        out.println("|" + filePath + "|RESULT=SUCCESS|");
    }

    //
    // Updates the existing file.
    //
    protected void doUpdateAction(HttpServletRequest request,
                                  HttpServletResponse response)
            throws ServletException,
            com.queplix.core.integrator.security.SecurityException,
            IOException {

        // Init.s
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);
        List items = getPostItems(request);

        // Get file parameters.
        Long id = null;
        FileItem fileItem = null;
        for(Iterator it = items.iterator(); it.hasNext();) {
            FileItem item = (FileItem) it.next();
            if(!item.isFormField()) {
                fileItem = item;
            } else if(item.getFieldName().equals("id")) {
                id = new Long(item.getString());
            }
        }

        // Check for the required parameters.
        if(fileItem == null) {
            throw new IncorrectParameterException("file");
        }
        if(id == null) {
            throw new IncorrectParameterException("id");
        }

        // Replace the file.
        manager.replaceFile(id.longValue(), fileItem);

        // Send HTML to close the window on client.
        response.setContentType(ServletHelper.CONTENT_TYPE_HTML);
        response.getWriter().print("<script>window.close()</script>");
    }


    //
    // Deletes the file(s).
    //
    protected void doDeleteAction(HttpServletRequest request,
                                  HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);

        long[] ids = ServletHelper.getAllParamValuesAsLong(request, "id");
        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG("The IDs of files to delete: " + StringHelper.toString(
                    ids));
        }

        // Delete the file(s) by ID.
        for(int i = 0; i < ids.length; i++) {
            manager.deleteFile(ids[i]);
        }
    }


    //
    // Synchronizes the folders tree with the related database tables.
    //
    protected void doSyncAction(HttpServletRequest request,
                                HttpServletResponse response)
            throws ServletException, SecurityException, IOException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);

        // Do sync.
        manager.syncDirectories();
    }


    //
    // Synchronize files action
    //
    protected void doSyncFilesAction(HttpServletRequest request,
                                     HttpServletResponse response)
            throws IOException, SecurityException, ServletException {

        // Init.
        LogonSession ls = WebLoginManager.getLogonSession(request);
        FileUploadManager manager = new FileUploadManager(ls);

        // Do sync.
        manager.syncFiles();
    }

    // ========================================================= Protected methods

    // Output file content.

    protected void outFile(FileManager.FileInfo fi,
                           boolean saveOnClientSide,
                           HttpServletResponse response)
            throws ServletException, IOException {

        if(logger.getLogger().isDebugEnabled()) {
            logger.DEBUG("File data:");
            logger.DEBUG("		path: " + fi.getFilePath());
            logger.DEBUG("		save on client side?: " + saveOnClientSide);
        }

        if(fi.getData() != null) {
            if(saveOnClientSide) {
                File file = new File(fi.getFilePath());
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + file.getName());
            }
            response.setContentLength(fi.getData().length);
            response.getOutputStream().write(fi.getData());
        } else {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        }
    }

    // Output file content.
    protected void outFile(byte[] data,
                           HttpServletResponse response)
            throws ServletException, IOException {

        if(data != null) {
            response.setContentLength(data.length);
            response.getOutputStream().write(data);
        } else {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
        }
    }

    // Get the file items from thePOST HTTP request.
    protected List getPostItems(HttpServletRequest request)
            throws ServletException {
        List items = null;
        try {
            DiskFileUpload fileUpload = new DiskFileUpload();
            fileUpload.setSizeMax(maxUploadSize);
            items = fileUpload.parseRequest(request);
        } catch (FileUploadException ex) {
            throw new ServletException(ex);
        }
        return items;
    }
}
