package com.ejweb.modules.upload.service;

import com.ejweb.conf.GConstants;
import com.ejweb.core.base.BaseService;
import com.ejweb.core.util.Util;
import com.ejweb.core.utils.IdWorker;
import com.ejweb.core.utils.PathFormatUtils;
import com.ejweb.modules.upload.bean.UploadBean;
import com.ejweb.modules.upload.bean.UploadFileBean;
import com.ejweb.modules.upload.dao.UploadDao;
import com.ejweb.modules.upload.entity.UploadEntity;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 文件上传统一处理Service
 * 
 * @team IT Team
 * @author renmb
 * @version 1.0
 * @time  2016年7月24日
 */
@Service
@Transactional(readOnly=true)
public class UploadService extends BaseService<UploadDao> {
    
    private static Logger LOG = Logger.getLogger(UploadService.class);
    private static final String PATH_FORMAt = GConstants.getValue("file.path.format", "{yyyy}{mm}{dd}");
    
    /**
     * 使用Spring的CommonsMultipartFile处理文件，每个表单的input名称必须为file
     * 
     * @author renmb
     * @time  2016年7月24日
     * @param files
     * @param moduleName
     * @param appCode
     * @param clientip
     * @return
     */
    @Transactional(readOnly = false)
    public List<UploadFileBean> addMultipartFile(CommonsMultipartFile[] files,
                                                 String moduleName, String appCode, String clientip){
        
        UploadBean uploadFile = new UploadBean();
        uploadFile.setModule(moduleName);
        uploadFile.setAppCode(appCode);
        
        return this.addMultipartFile(files, uploadFile, clientip);
    }
    /**
     * 使用Spring的CommonsMultipartFile处理文件，每个表单的input名称必须为file
     * 
     * @author renmb
     * @time  2016年7月24日
     * @param files
     * @param uploadFile
     * @param clientip
     * @return
     */
    @Transactional(readOnly = false)
    public List<UploadFileBean> addMultipartFile(CommonsMultipartFile[] files, UploadBean uploadFile, String clientip){
        if(files == null || files.length == 0){
            return null;
        }
        List<UploadFileBean> uploadFiles = null;
//        boolean isVerifyFileName = true;// 是否需要验证文件名称
        String message = validate(uploadFile);
        if(message == null){
//            uploadFile.setSize(0);
//            uploadFile.setUrl(GConstants.FILE_PREFIX_URL);
            uploadFiles = new ArrayList<UploadFileBean>(6);
            uploadFile.setFiles(uploadFiles);
//            uploadFiles = uploadFile.getFiles();
//            if(uploadFiles == null){// 如果没有传文件列表则新建一个列表
//                uploadFiles = new ArrayList<UploadFileBean>(6);
//                uploadFile.setFiles(uploadFiles);
////                isVerifyFileName = false;
//            }
            
//            String moduleName = uploadFile.getModule();// 模块名称
//            if(StringUtils.isBlank(moduleName)){// 如果没有传则默认保存到files下面
//                moduleName = "files";
//            } else{
//                moduleName = moduleName.replaceAll("^/+|/+$|[^0-9|a-z|A-Z|/]+", "");// 替换非法字符串
//                if(moduleName.length() == 0 || moduleName.length()>64)// 如果没有传则默认保存到files下面
//                    moduleName = "files";
//            }
//            uploadFile.setModule(moduleName);
            
            // 生成本次会话的随机数随机数
            String sessionId = DigestUtils.md5Hex(Util.getRandom(100, 999)+":"
                    +System.currentTimeMillis()+":"+Util.getRandom(100, 999));
//            uploadFile.setSessionId(sessionId);
            for (CommonsMultipartFile file:files) {// 遍历所有提交的文件
                try {
                    String originalFilename = file.getOriginalFilename();
                    if(StringUtils.isNotBlank(originalFilename)){// 文件扩展名称不能为NULL
                        InputStream in = file.getInputStream();
                        
                        String contentType = uploadFile.getContentType();
                        UploadFileBean fileBean = this.addUploadFile(sessionId, in, file.getName(), originalFilename, contentType,
                                uploadFile.getModule(), uploadFile.getAppCode(), clientip, uploadFile.getOrientation());
                        uploadFiles.add(fileBean);
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                }
//                if(file !=null){
//                    // 统一文件处理
//                    addUploadFile(uploadFiles, uploadFile, isVerifyFileName,
//                            sessionId, file, clientip);
//                }
            }
            return uploadFiles;
        }
        return uploadFiles;
    }
    /**
     * 使用Spring的CommonsMultipartFile处理文件，每个表单的input名称必须为file
     * 
     * @author renmb
     * @time  2016年7月24日
     * @param request
     * @param moduleName
     * @param appCode
     * @param clientip
     * @return
     */
    @Transactional(readOnly = false)
    public List<UploadFileBean> addMultipartFile(HttpServletRequest request,
                                                 String moduleName, String appCode, String clientip){
        
        UploadBean uploadFile = new UploadBean();
        uploadFile.setModule(moduleName);
        uploadFile.setAppCode(appCode);
        
        return this.addMultipartFile(request, uploadFile, clientip);
    }
    /**
     * 使用Spring的CommonsMultipartResolver处理文件，每个input的名称都不一致
     * 
     * @author renmb
     * @time  2016年7月24日
     * @param request
     * @param uploadFile
     * @param clientip
     * @return
     */
    @Transactional(readOnly = false)
    public List<UploadFileBean> addMultipartFile(HttpServletRequest request,
                                                 UploadBean uploadFile, String clientip){
        try {
            List<UploadFileBean> uploadFiles = null;
//            boolean isVerifyFileName = true;// 是否需要验证文件名称
            String message = validate(uploadFile);
            if(message == null){
//                uploadFile.setSize(0);
                // 创建一个通用的多部分解析器
                CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                        request.getSession().getServletContext());
                // 判断 request 是否有文件上传,即多部分请求
                if (multipartResolver != null && multipartResolver.isMultipart(request)) {
                    
//                    uploadFile.setUrl(GConstants.FILE_PREFIX_URL);
                    uploadFiles = new ArrayList<UploadFileBean>(6);
                    uploadFile.setFiles(uploadFiles);
//                    if(uploadFiles == null){// 如果没有传文件列表则新建一个列表
//                        uploadFiles = new ArrayList<UploadFileBean>(6);
//                        uploadFile.setFiles(uploadFiles);
//                        isVerifyFileName = false;
//                    }
                    
                    String moduleName = uploadFile.getModule();// 模块名称
//                    if(StringUtils.isBlank(moduleName)){// 如果没有传则默认保存到files下面
//                        moduleName = "files";
//                    } else{
//                        moduleName = moduleName.replaceAll("^/+|/+$|[^0-9|a-z|A-Z|/]+", "");// 替换非法字符串
//                        if(moduleName.length() == 0 || moduleName.length()>64)// 如果没有传则默认保存到files下面
//                            moduleName = "files";
//                    }
//                    uploadFile.setModule(moduleName);
                    
                    // 生成本次会话的随机数随机数
                    String sessionId = DigestUtils.md5Hex(Util.getRandom(100, 999)+":"
                            +System.currentTimeMillis()+":"+Util.getRandom(100, 999));
//                    uploadFile.setSessionId(sessionId);
                    // 转换成多部分request
                    MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
                    // 取得request中的所有文件名
                    Iterator<String> iter = multiRequest.getFileNames();
                    while (iter.hasNext()) {
                        try {
                            MultipartFile file = multiRequest.getFile(iter.next());
                            if (file != null) {// 取得上传文件
                                
                                String originalFilename = file.getOriginalFilename();
                                if(StringUtils.isNotBlank(originalFilename)){// 文件扩展名称不能为NULL
                                    InputStream in = file.getInputStream();
                                    String contentType = uploadFile.getContentType();
                                    UploadFileBean fileBean = this.addUploadFile(sessionId, in, file.getName(), originalFilename, contentType,
                                            moduleName, uploadFile.getAppCode(), clientip, "");
                                    uploadFiles.add(fileBean);
                                }
                            }
                        } catch (Exception e) {
                            // TODO: handle exception
                        }
                        // 取得上传文件
//                        MultipartFile file = multiRequest.getFile(iter.next());
//                        file.getName();
//                        if (file != null) {
//                            // 统一文件处理
//                            addUploadFile(uploadFiles, uploadFile, isVerifyFileName,
//                                    sessionId, file, clientip);
//                        }
                    }
                }
                return uploadFiles;
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
        return null;
    }

    /**
     * 通过文件流上传文件
     *
     * @param sessionId
     * @param in
     * @param inputName
     * @param originalFilename
     * @param contentType
     * @param moduleName
     * @param appCode
     * @param clientip
     * @return
     */
    @SuppressWarnings("resource")
    @Transactional(readOnly = false)
    public UploadFileBean addUploadFile(String sessionId, InputStream in, String inputName, String originalFilename,
                                        String contentType, String moduleName, String appCode, String clientip, String orientation) {
        
        OutputStream os = null;
        ByteArrayOutputStream baos = null;
        UploadEntity fileUploadEntity=null;
//        InputStream in = null;
        try {
//            String originalFilename = file.getOriginalFilename();
            if(StringUtils.isBlank(originalFilename)){// 文件扩展名称不能为NULL
                LOG.debug("无法获取文件名称："+originalFilename);
                return null;
            }
            if("blob".equals(originalFilename)){// 上传的二进制文件，兼容H5文件无法上传的情况
                if(StringUtils.isEmpty(contentType)){// 默认为MP3格式
                    
                    originalFilename = "blob.mp3";
                } else {
                    
                    originalFilename = "blob."+contentType;
                }
            }
            String extesionName = Util.getExtensionName(originalFilename);
            if(extesionName == null || extesionName.length() == 0){// 文件扩展名称不能为NULL
                LOG.debug("无法获取文件扩展名："+originalFilename);
            }
            UploadFileBean fileBean = null;
//            uploadFile.setSize(uploadFile.getSize()+1);
            if(extesionName == null || 
                    GConstants.FILE_FILTERS.get(extesionName.toLowerCase()) == null){// 不允许上传的文件
                
                LOG.debug("此类型文件不允许上传："+originalFilename);
                fileBean = new UploadFileBean();
                fileBean.setInputName("");
                fileBean.setPath(null);
                fileBean.setName(originalFilename);
                fileBean.setExtesion(extesionName);
                fileBean.setStatus(2);
                return fileBean;
            }
            
            // 拿到上传文件的输入流
//            in = request.getInputStream();
            in = new BufferedInputStream(in);
            baos = new ByteArrayOutputStream();
            byte[] buf = new byte[GConstants.BUFFER_SIZE];
            // 以写字节的方式写文件
            int size = in.read(buf);
            while (size != -1) {
                baos.write(buf, 0, size);
                size = in.read(buf);
            }
            byte[] data     = baos.toByteArray();
            
//            byte[] data = IOUtils.toByteArray(in);
            // 待扩展名称的MOD5
            String md5      = DigestUtils.md5Hex(data)+extesionName;
            
            fileBean = new UploadFileBean();
            fileBean.setName(originalFilename);
            if(StringUtils.isBlank(inputName)){
                fileBean.setInputName("");
            } else {
                fileBean.setInputName(inputName);
            }
            fileBean.setSize(data.length);
            fileBean.setMd5(md5);
            fileBean.setExtesion(extesionName);
            
            if(StringUtils.isBlank(moduleName)){// 如果没有传则默认保存到files下面
                moduleName = "files";
            } else{
                moduleName = moduleName.replaceAll("^/+|/+$|[^0-9|a-z|A-Z|/]+", "");// 替换非法字符串
                moduleName = moduleName.replaceAll("[\\|//]+", "/");
                if(moduleName.length() == 0 || moduleName.length()>64)// 如果没有传则默认保存到files下面
                    moduleName = "files";
            }
            // 文件保存路径：基本路径+模块名称+日期
            String baseDatePath = PathFormatUtils.parse(PATH_FORMAt);//FORMAT.format(System.currentTimeMillis());
            String basePath     = moduleName+ GConstants.FS+extesionName.replaceAll("\\.", "")+ GConstants.FS;
            // 上传文件基本地址
            File baseUploadDir = new File(GConstants.FILE_UPLOAD_DIR, baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_ACTUALS+ GConstants.FS+basePath);
            if(!baseUploadDir.exists()){// 如果文件夹不存在则创建
                baseUploadDir.mkdirs();
            }
            
//            String prefixFilename = "";
//            if(StringUtils.isBlank(GConstants.getValue("file.prefix.filename.format")) == false){
//                try {
//                    SimpleDateFormat PRE_FORMAT = new SimpleDateFormat(GConstants.getValue("file.prefix.filename.format"));
//                    prefixFilename = PRE_FORMAT.format(System.currentTimeMillis());// 文件名称前面部分
//                } catch (Exception e) {
//                    // TODO: handle exception
//                }
//            }
//            String sessionId = DigestUtils.md5Hex(Util.getRandom(100, 999)+":"
//                    +System.currentTimeMillis()+":"+Util.getRandom(100, 999));
            
            // 文件保存地址
            File uploadFilePath = new File(baseUploadDir, md5);
            LOG.info("原文件服务器绝对路径："+uploadFilePath);
            // 将数据保存到指定文件
            os = new FileOutputStream(uploadFilePath);
            os = new BufferedOutputStream(os);
            os.write(data);
            os.flush();
            os.close();
            this.copyToMultiplePaths(uploadFilePath);
            
            // 设置文件相关信息
            fileUploadEntity = new UploadEntity();
            // 如果上传文件为图片则获取图片的宽度高度
            if(GConstants.FILE_FILTERS.get(extesionName) != null &&
                    GConstants.FILE_FILTERS.get(extesionName)){
                BufferedImage image = this.getBufferedImage(data);
                if(image != null){// 生成图片缩略图
                    fileBean.setWidth(image.getWidth());
                    fileBean.setHeight(image.getHeight());
                    if(GConstants.FILE_ALLOW_THUMBS){// 图片压缩保存
                        
                        int with   = GConstants.getIntValue("file.image.thumb.width", 640);
                        int height = GConstants.getIntValue("file.image.thumb.height", 640);
                        if(image.getWidth()>with || image.getHeight()>height){
                            
                            File baseUploadThumbsDir = new File(GConstants.FILE_UPLOAD_DIR,
                                    baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_THUMBS+ GConstants.FS+basePath);
                            if(!baseUploadThumbsDir.exists()){// 如果文件夹不存在则创建
                                baseUploadThumbsDir.mkdirs();
                            }
                            File uploadThumbsFilePath = new File(baseUploadThumbsDir, md5);
                            LOG.info("缩略图文件服务器绝对路径："+uploadThumbsFilePath);
                            
                            Thumbnails.of(image).size(with, height)
                            .keepAspectRatio(true).outputQuality(1f)
                            .toFile(uploadThumbsFilePath);
                            // 设置缩略图地址前缀
                            fileBean.setThumbs(baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_THUMBS+ GConstants.FS+basePath+md5);
                            fileUploadEntity.setThumbs(baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_THUMBS+ GConstants.FS+basePath+md5);

                            this.copyToMultiplePaths(uploadThumbsFilePath);
                        } else{
                            
                            fileBean.setThumbs(baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_ACTUALS+ GConstants.FS+basePath+md5);
                            fileUploadEntity.setThumbs(fileBean.getThumbs());
                        }
                    }
                }
            }
            fileUploadEntity.setName(fileBean.getName());
            fileUploadEntity.setModule(moduleName);
            fileUploadEntity.setMd5(md5);
            fileUploadEntity.setExtesion(extesionName);
            fileUploadEntity.setPath(baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_ACTUALS+ GConstants.FS+basePath+md5);
            fileUploadEntity.setAppCode(appCode);
            fileUploadEntity.setWidth(fileBean.getWidth());
            fileUploadEntity.setHeight(fileBean.getHeight());
            fileUploadEntity.setSize(data.length);
            fileUploadEntity.setClientip(clientip);
            fileUploadEntity.setSessionId(sessionId);
            fileUploadEntity.setInputName(fileBean.getInputName());
            fileUploadEntity.setFileId(IdWorker.getNextId());
            fileUploadEntity.setOrientation(orientation);
            // 保存文件到数据库
            dao.addUploadFile(fileUploadEntity);
            
            fileBean.setExtesion(extesionName);
            fileBean.setSize(data.length);
            fileBean.setMd5(md5);
            fileBean.setPath(baseDatePath+ GConstants.FS+ GConstants.FILE_IMAGE_ACTUALS+ GConstants.FS+basePath+md5);
            fileBean.setStatus(1);
            return fileBean;
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(os);
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(baos);
        }
        return null;
    }
    /**
     * 获取图片文件
     * 
     * @author renmb
     * @time  2016年7月24日
     * @param data
     * @return
     */
    public BufferedImage getBufferedImage(byte[] data){
        BufferedImage image = null;
        try {
            image = ImageIO.read(new ByteArrayInputStream(data));
            return image;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 将文件复制到其它备份磁盘
     * @param file
     */
    private void copyToMultiplePaths (final File file){

        final String dirs = GConstants.getValue("file.upload.dirs");
        if(StringUtils.isNotEmpty(dirs)){
            try {
                Thread thread = new Thread(new Runnable(){
                    @Override
                    public void run() {
                        String basePath = file.getAbsolutePath().replace(GConstants.FILE_UPLOAD_DIR, "");
                        String[] paths = dirs.split(";");
                        for (String path:paths){
                            InputStream  input = null;
                            OutputStream output = null;
                            try{
                                File newFilePath = new File(path, basePath);
                                if(!newFilePath.getParentFile().exists()){// 如果文件夹不存在则创建
                                    newFilePath.getParentFile().mkdirs();
                                }
                                LOG.debug("UploadService copyToMultiplePaths: "+file.getAbsolutePath()+" TO "+newFilePath.getAbsolutePath());
                                input = new FileInputStream(file);
                                output = new FileOutputStream(newFilePath);
                                IOUtils.copy(input, output);
                            } catch (Exception e){
                                IOUtils.closeQuietly(input);
                                IOUtils.closeQuietly(output);
                            }
                        }
                    }
                });
                thread.start();
            } catch (Exception e){

                LOG.error(e.getMessage());
            }
        }
    }

    public List<UploadEntity> getFileByPath(String path) {
        return dao.getFileByPath(path);
    }
}
