/* UtilLib_Servlet_MultiPart - Copyright (c) 2009-2025 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.servlets.multipart;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import net.mapeadores.util.exceptions.NestedLibraryException;
import net.mapeadores.util.request.FileValue;
import net.mapeadores.util.servlets.MultiPartParser;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.servlet.ServletRequestContext;


/**
 *
 * @author Vincent Calame
 */
public class DefaultMultiPartParser implements MultiPartParser {

    public DefaultMultiPartParser() {
    }

    @Override
    public boolean isMultiPartContentRequest(HttpServletRequest request) {
        ServletRequestContext requestContext = new ServletRequestContext(request);
        return ServletFileUpload.isMultipartContent(requestContext);
    }

    @Override
    public Map<String, Object> parseMultiPartContentRequest(HttpServletRequest request) {
        DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
        ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
        servletFileUpload.setSizeMax(-1);
        try {
            List fileItemList = servletFileUpload.parseRequest(request);
            MultiPartValueMapBuilder builder = new MultiPartValueMapBuilder(request.getCharacterEncoding());
            for (Iterator it = fileItemList.iterator(); it.hasNext();) {
                FileItem fileItem = (FileItem) it.next();
                builder.addFileItem(fileItem);
            }
            return builder.getMultiPartValueMap();
        } catch (FileUploadException fue) {
            throw new NestedLibraryException(fue);
        }
    }


    private class MultiPartValueMapBuilder {

        Map<String, Object> multiPartValueMap = new HashMap<String, Object>();
        String charsetName;

        MultiPartValueMapBuilder(String charsetName) {
            if (charsetName == null) {
                this.charsetName = "UTF-8";
            } else {
                this.charsetName = charsetName;
            }
        }

        Map<String, Object> getMultiPartValueMap() {
            return multiPartValueMap;
        }

        private void addFileItem(FileItem fileItem) {
            if (fileItem.isFormField()) {
                addString(fileItem);
            } else {
                addFileValue(fileItem);
            }
        }

        private void addString(FileItem fileItem) {
            String name = fileItem.getFieldName();
            String value;
            try {
                value = fileItem.getString(charsetName);
            } catch (IOException ioe) {
                value = fileItem.getString();
            }
            Object current = multiPartValueMap.get(name);
            if (current == null) {
                multiPartValueMap.put(name, value);
            } else if (current instanceof String[]) {
                String[] currentArray = (String[]) current;
                int length = currentArray.length;
                String[] newArray = new String[length + 1];
                System.arraycopy(currentArray, 0, newArray, 0, length);
                newArray[length] = value;
                multiPartValueMap.put(name, newArray);
            } else if (current instanceof String) {
                String[] array = new String[2];
                array[0] = (String) current;
                array[1] = value;
                multiPartValueMap.put(name, array);
            }
        }

        private void addFileValue(FileItem fileItem) {
            String name = fileItem.getFieldName();
            FileValue value = new InternalFileValue(fileItem);
            Object current = multiPartValueMap.get(name);
            if (current == null) {
                multiPartValueMap.put(name, value);
            } else if (current instanceof FileValue[]) {
                FileValue[] currentArray = (FileValue[]) current;
                int length = currentArray.length;
                FileValue[] newArray = new FileValue[length + 1];
                System.arraycopy(currentArray, 0, newArray, 0, length);
                newArray[length] = value;
                multiPartValueMap.put(name, newArray);
            } else if (current instanceof FileValue) {
                FileValue[] array = new FileValue[2];
                array[0] = (FileValue) current;
                array[1] = value;
                multiPartValueMap.put(name, array);
            }
        }

    }


    private static class InternalFileValue implements FileValue {

        private final FileItem fileItem;

        InternalFileValue(FileItem fileItem) {
            this.fileItem = fileItem;
        }

        @Override
        public String getName() {
            return fileItem.getName();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return fileItem.getInputStream();
        }

        @Override
        public void free() {
            fileItem.delete();
        }

        @Override
        public long length() {
            return fileItem.getSize();
        }

    }

}
