package zmo.technology.lite.controllers;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.alibaba.fastjson.JSONObject;

import zmo.technology.lite.core.ZeroController;
import zmo.technology.lite.core.ZeroException;
import zmo.technology.lite.core.ZeroFolders.ZeroFolderEntity;
import zmo.technology.lite.core.ZeroRequest;
import zmo.technology.lite.core.ZeroRequestGuider;
import zmo.technology.lite.processors.ZeroFolderProcessor;

@Controller
@RequestMapping(ZeroFolderController.FOLDER_PREFIX)
public class ZeroFolderController extends ZeroController {

    public static final String FOLDER_PREFIX = "folder";

    public ZeroFolderController() {
        super(FOLDER_PREFIX);
    }

    @Autowired
    private DataSource dataSource;

    public static final Set<String> STATIC_HANDLERS;
    static {
        STATIC_HANDLERS = new HashSet<>();
        STATIC_HANDLERS.add("up");
        STATIC_HANDLERS.add("rm");
    }

    @Override
    protected String handlerNameConstructor(ZeroRequestGuider guider) throws ZeroException {
        if (guider.getPaths().size() < 1)
            throw new ZeroException(404, "no handlers");
        if (STATIC_HANDLERS.contains(guider.getPaths().get(0)))
            return guider.getPaths().get(0);
        return "media";
    }

    @RequestMapping(GENERAL_HANDLER_PATH)
    public void general(HttpServletRequest request, HttpServletResponse response) throws Exception {
        proxyHandler(dataSource, request, response);
    }

    private static final int THRESHOLD_SIZE = 1024 * 1024;
    private static final int FILE_MAX_SIZE = 1024 * 1024 * 50;
    private static final int MAX_SIZE = 1024 * 1024 * 50;

    public void up(ZeroRequestGuider guider) throws Exception {
        if (!ServletFileUpload.isMultipartContent(guider.getRequest()))
            throw new ZeroException(" no support ");

        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(THRESHOLD_SIZE);
        factory.setRepository(new File(guider.getRequest().getServletContext().getRealPath("/tmp/")));

        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        fileUpload.setFileSizeMax(FILE_MAX_SIZE);
        fileUpload.setSizeMax(MAX_SIZE);
        fileUpload.setHeaderEncoding("utf-8");

        ZeroFolderProcessor processor = ZeroFolderProcessor.build(guider);
        JSONObject expands = new JSONObject();

        List<FileItem> items = fileUpload.parseRequest(new ServletRequestContext(guider.getRequest()));
        for (FileItem fileItem : items) {
            if (!fileItem.isFormField()) {
                InputStream inputStream = fileItem.getInputStream();
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                try {
                    int len = -1;
                    byte[] buffer = new byte[1024];
                    while ((len = inputStream.read(buffer)) != -1)
                        outputStream.write(buffer, 0, len);

                    ZeroFolderEntity entity = new ZeroFolderEntity();
                    entity.getFeatures().put("size", fileItem.getSize());
                    entity.setName(fileItem.getName());
                    entity.setMineType(fileItem.getContentType());
                    entity.setDatas(outputStream.toByteArray());
                    processor.addFolder(entity);

                    expands.put(fileItem.getFieldName(), entity.uuIDString());
                } catch (Exception e) {
                    throw e;
                } finally {
                    outputStream.close();
                    inputStream.close();
                    fileItem.delete();
                }
            }
        }
        guider.write(success(new ArrayList<>(), expands));
    }

    private static final String EXPANDS_ENTITY_ID = "entityID";

    public void rm(ZeroRequestGuider guider) throws Exception {
        ZeroRequest<?> request = ZeroRequest.build(guider.getRawData());
        if (!request.getExpands().containsKey(EXPANDS_ENTITY_ID))
            throw new ZeroException("`expands.entityID` not found");
        ZeroFolderProcessor processor = ZeroFolderProcessor.build(guider);
        processor.rmFolder(request.getExpands().getString(EXPANDS_ENTITY_ID));
        guider.write(success());
    }

    public void media(ZeroRequestGuider guider) throws Exception {
        ZeroFolderProcessor processor = ZeroFolderProcessor.build(guider);

        ZeroFolderEntity entity = guider.getPaths().size() == 1
                ? processor.fetchFolder(URLDecoder.decode(guider.getPaths().get(0), "utf-8"))
                : processor.fetchFolderDataByPath(URLDecoder.decode(String.join("/", guider.getPaths()), "utf-8"));

        String name = new String(entity.getName().getBytes("UTF-8"), "ISO-8859-1");
        guider.getResponse().reset();
        if (guider.getQueryParams().containsKey("down"))
            guider.getResponse().addHeader("Content-Disposition", String.format("attachment;filename=\"%s\"", name));
        else
            guider.getResponse().addHeader("Content-Disposition", String.format("filename=\"%s\"", name));

        if (entity.getFeatures() != null && entity.getFeatures().containsKey("size"))
            guider.getResponse().addHeader("Content-Length", "" + entity.getFeatures().getIntValue("size"));
        else
            guider.getResponse().addHeader("Content-Length", "" + entity.getDatas().length);

        OutputStream outputStream = new BufferedOutputStream(guider.getResponse().getOutputStream());
        guider.getResponse().setContentType(entity.getMineType() + ";charset=utf-8");

        guider.getResponse().setCharacterEncoding("UTF-8");

        outputStream.write(entity.getDatas());
        outputStream.flush();
        outputStream.close();
    }
}
