package kr.co.kihyun.io;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import kr.co.kihyun.moumi.MoumiConfig;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import kr.co.kihyun.lang.MString;

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.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MultipartRequest extends HttpServletRequestWrapper {
	private static final Logger LOG = LoggerFactory.getLogger(MultipartRequest.class);
	private final Map<String, List<FileItem>> formParams = new LinkedHashMap<String, List<FileItem>>();
        
        private static final String savePath = MoumiConfig.getFileDirectory().getPath().toString();

      //v2. 5.static final 필드 변조 가능성 : Update by KWON,HAN         
        //50.public static을 통한 선언 오류(CWE-500) Update by YOUNGJUN,CHO
        //public static int maxSize=50;
//	public static final int maxSize=50;
        private static final int maxSize=50;
        //================================================
        
	public MultipartRequest(HttpServletRequest request) throws IOException {
		this(request, savePath);
//		this(request, System.getProperty("java.io.tmpdir"));
	}

	public MultipartRequest(HttpServletRequest request, String savePath) throws IOException {
		this(request, savePath, maxSize * 1024 * 1024);
	}
        
        //50.public static을 통한 선언 오류(CWE-500) Add by YOUNGJUN,CHO
        // maxSize 를 final 로 선언하여, 다른 클래스에서 제한용량을 따로 지정할 경우에 final 값을 변경하지 못하므로 메서드 오버로딩으로 처리함.
        public MultipartRequest(HttpServletRequest request, long fileSizeMax) throws IOException {
		this(request, savePath, fileSizeMax);
	}
        //++++++++++++++++++++++++++++++++++++++++++++++++
        
	@SuppressWarnings("unchecked")
	public MultipartRequest(HttpServletRequest request, String savePath, long fileSizeMax) throws IOException {
		super(request);
		request.setCharacterEncoding("UTF-8");

		DiskFileItemFactory factory = new DiskFileItemFactory();

//파일명을 체크하는 것이 아니므로 오탐이다                
//                //10.자원 삽입(임의 파일 접근)_CWE-99 : Add by KWON,HAN
//                LOG.debug("savePath: {}", savePath); 
//                if(savePath.contains("..") || savePath. contains("/")) { // 특수문자열 검증
//                    //LOG.debug("MultipartRequest MultipartRequest ===");
//                    //LOG.debug("10.자원 삽입(임의 파일 접근)_CWE-99 : Test OK {}", savePath);
//                    //LOG.debug("=====================================");
//                    throw new IOException("{} 파일명에 특수문자(.. 또는 /)이 존재합니다." + savePath.toString());
//                }
//                //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
                
                
		File tmpDir = new File(savePath);
                
                // 43.권한 미설정의 위험성(mkdir)_CWE-379 : Add by YOUNGJUN,CHO
                tmpDir.setReadable(true, true);
                tmpDir.setWritable(true, true);
                tmpDir.setExecutable(true, true);
                //++++++++++++++++++++++++++++++++++++++++++++++++
                
		factory.setRepository(tmpDir);
		if (!tmpDir.exists() && !tmpDir.mkdir()) {
			factory.setRepository(new File(savePath));
		}
		LOG.debug("SAVEPATH: ", factory.getRepository());
		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setFileSizeMax(fileSizeMax);
		upload.setSizeMax(-1);

		try {
			LOG.debug("request: {}", request);
			for (FileItem item : (List<FileItem>) upload.parseRequest(request)) {
				if (formParams.get(item.getFieldName()) != null) {
					formParams.get(item.getFieldName()).add(item);
				} else {
					List<FileItem> list = new ArrayList<FileItem>();
					list.add(item);
					formParams.put(item.getFieldName(), list);
				}
				LOG.debug("BUILD MULTIPART REQUEST item {} added for key {}", item, item.getFieldName());
			}
		} catch (FileUploadException fue) {
			fue.printStackTrace();
			throw new IOException("Cannot parse underlying request: " + fue.toString());
		}
	}

	@Override
	public Enumeration<String> getParameterNames() {
		return Collections.enumeration(formParams.keySet());
	}

	@Override
	public String getParameter(String aName) {
		List<FileItem> values = formParams.get(aName);
		if (values == null) {
			//LOG.debug("values null. param {}: {}", aName, super.getParameter(aName));
			return super.getParameter(aName);
		} else if (values.isEmpty()) {
			return "";
		} else {
			if (values.get(0).isFormField()) {
				try {
					return values.get(0).getString("UTF-8");
				} catch (UnsupportedEncodingException e) {
					LOG.warn("{}, UTF-8 is unsupported. So system default encoding will be used.", e.getMessage());
					return values.get(0).getString();
				}
			} else {
				if (MString.isNull(values.get(0).getName()))
					return "";
				String delim = System.getProperty("file.separator").equals("\\") ? "\\\\" : System
						.getProperty("file.separator");
				String[] fileName = values.get(0).getName().split(delim);
				return fileName[fileName.length > 1 ? fileName.length - 1 : 0];
			}
		}
	}

	@Override
	public String[] getParameterValues(String aName) {
		List<FileItem> items = formParams.get(aName);
		String[] result = null;
		if (items != null) {
			result = new String[items.size()];
			for (int i = 0; i < result.length; i++) {
				try {
					result[i] = items.get(i).getString("UTF-8");
				} catch (UnsupportedEncodingException e) {
					LOG.warn("{}, UTF-8 is unsupported. So system default encoding will be used.", e.getMessage());
					result[i] = items.get(i).getString();
				}
			}
		}
		return result;
	}

	@Override
	public Map<String, String[]> getParameterMap() {
		Map<String, String[]> result = new LinkedHashMap<String, String[]>();
		for (String paramName : formParams.keySet())
			result.put(paramName, this.getParameterValues(paramName));
		return result;
	}

	public List<FileItem> getFileItems(String aFieldName) {
		return formParams.get(aFieldName);
	}

	public FileItem getFileItem(String aFieldName) {
		List<FileItem> values = formParams.get(aFieldName);
		if (values != null && !values.isEmpty())
			return values.get(0);
		return null;
	}
}
