/*******************************************************************************
 * Copyright 2Fi Business Solutions Ltd.
 *
 * This code is copyrighted. Under no circumstances should any party, people,
 * or organization should redistribute any portions of this code in any form,
 * either verbatim or through electronic media, to any third parties, unless
 * under explicit written permission by 2Fi Business Solutions Ltd.
 ******************************************************************************/
package com.ffii.tbms.file.web;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;

import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.ser.std.JsonValueSerializer;
import com.ffii.core.setting.service.SettingsService;
import com.ffii.core.utils.FileUtils;
import com.ffii.core.utils.MapUtils;
import com.ffii.core.utils.NumberUtils;
import com.ffii.core.utils.Params;
import com.ffii.core.utils.StringUtils;
import com.ffii.core.utils.web.ServletRequestUtils;
import com.ffii.core.web.AbstractController;
import com.ffii.core.web.view.AbstractView;
import com.ffii.core.web.view.json.JsonView;
import com.ffii.tbms.common.MaterialBand;
import com.ffii.tbms.common.service.MaterialService;
import com.ffii.tbms.file.File;
import com.ffii.tbms.file.FileBlob;
import com.ffii.tbms.file.FileRef;
import com.ffii.tbms.file.FileRefType;
import com.ffii.tbms.file.service.FileService;
import com.ffii.tbms.log.LogUtils;
import com.ffii.tbms.order.Order;
import com.ffii.tbms.order.service.OrderService;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

/**
 * Upload File Controller
 *
 * @see File
 * @see FileBlob
 * @see FileRef
 */
@Controller
@RequestMapping(value = "/file")
public class UploadFileController extends AbstractController {

	@Autowired
	private SettingsService settingsService;

	@Autowired
	private FileService fileService;

	@Autowired
	private OrderService orderService;

	@Autowired
	private MaterialService materialService;

	private static final boolean OVERWRITE_SAME_FILENAME = false;

	private static final int DEFAULT_UPLOAD_MAX_FILE_SIZE_MB = 50;

	@RequestMapping(value = "/ul", method = RequestMethod.POST)
	public String uploadFile(HttpServletRequest request, Model model,
			@RequestParam int refId, @RequestParam String refType, @RequestParam(defaultValue = StringUtils.EMPTY) String refCode,
			@RequestParam MultipartFile multipartFile) throws Exception {

		// get file upload max file size setting
		int uploadMaxFileSize = settingsService.getInt("FILE.upload.maxFileSize", DEFAULT_UPLOAD_MAX_FILE_SIZE_MB) * 1024 * 1024;

		Boolean success = Boolean.TRUE;

		// only proceed if multipartFile is not null, and has file size
		if (multipartFile != null && multipartFile.getSize() > 0 && multipartFile.getSize() <= uploadMaxFileSize) {

			// DEBUG LOG
			logger.info("multipartFile.getSize() = " + multipartFile.getSize());

			File file = new File();
			file.setFilename(multipartFile.getOriginalFilename());
			// file.setMimetype(multipartFile.getContentType());
			file.setMimetype(FileUtils.guessMimetype(file.getFilename()));
			file.setFilesize(multipartFile.getSize());
			file.setDescription(ServletRequestUtils.getTrimmedStringParameter(request, "description"));

			FileBlob fileBlob = new FileBlob();
			fileBlob.setBytes(multipartFile.getBytes());

			FileRef fileRef = new FileRef();
			fileRef.setRefId(refId);
			fileRef.setRefType(refType);
			fileRef.setRefCode(refCode);

			// get height and width if mimetype is png or jpeg
			if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) {
				BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes()));
				if (image != null) {
					file.setImageHeight(image.getHeight());
					file.setImageWidth(image.getWidth());
				}
			}

			if (OVERWRITE_SAME_FILENAME) {
				// search for existing file(s) with the same refType, refId, and filename
				List<Map<String, Object>> existingFiles = fileService.searchFiles(
						MapUtils.toHashMap("refType", refType, "refId", refId, "filename", file.getFilename()));

				// delete them if found
				for (Map<String, Object> existingFile : existingFiles) {
					fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"),
							(String) existingFile.get("refType"), (String) existingFile.get("skey"));
				}
			}

			// create UserFile
			file.setSkey(RandomStringUtils.randomAlphanumeric(16));
			fileService.saveFile(file);

			// create UserFileBlob
			fileBlob.setFileId(file.getId());
			fileService.saveFileBlob(fileBlob);

			// create UserFileRef
			fileRef.setFileId(file.getId());
			fileService.saveFileRef(fileRef);

			if(refType.equals(FileRefType.ORDER)){
				orderService.log(LogUtils.ACTION_CREATE,
					refId, 
					MapUtils.toHashMap("photoFileId", file.getId(), "filename",file.getFilename()));
			}

		} else {
			success = Boolean.FALSE;

			// if not success, return msg to client
			model.addAttribute(Params.MSG, getMessageSourceAccessor().getMessage("Upload Failed"));
		}

		model.addAttribute(Params.SUCCESS, success);

		return JsonView.NAME;
	}

	
	@RequestMapping(value = "/files/ul", method = RequestMethod.POST)
	public String uploadFiles(HttpServletRequest request, Model model,@RequestParam String refType,
	@RequestParam int refId, @RequestParam(defaultValue = StringUtils.EMPTY) String refCode, 
	@RequestParam List<MultipartFile> multipartFiles) throws ServletRequestBindingException, IOException {

		// get file upload max file size setting
		int uploadMaxFileSize = settingsService.getInt("FILE.upload.maxFileSize", DEFAULT_UPLOAD_MAX_FILE_SIZE_MB) * 1024 * 1024;

		for(MultipartFile multipartFile: multipartFiles){

			if (multipartFile != null && multipartFile.getSize() > 0 && multipartFile.getSize() <= uploadMaxFileSize) {
				logger.info("file size : "+ multipartFile.getSize());
				File file = new File();
				file.setFilename(multipartFile.getOriginalFilename());
				// file.setMimetype(multipartFile.getContentType());
				file.setMimetype(FileUtils.guessMimetype(file.getFilename()));
				file.setFilesize(multipartFile.getSize());
				file.setDescription(ServletRequestUtils.getTrimmedStringParameter(request, "description"));
	
				FileBlob fileBlob = new FileBlob();
				fileBlob.setBytes(multipartFile.getBytes());
	
				FileRef fileRef = new FileRef();
				fileRef.setRefId(refId);
				fileRef.setRefType(refType);
				fileRef.setRefCode(refCode);
	
				// get height and width if mimetype is png or jpeg
				if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) {
					BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes()));
					if (image != null) {
						file.setImageHeight(image.getHeight());
						file.setImageWidth(image.getWidth());
					}
				}
	
				if (OVERWRITE_SAME_FILENAME) {
					// search for existing file(s) with the same refType, refId, and filename
					List<Map<String, Object>> existingFiles = fileService.searchFiles(
							MapUtils.toHashMap("refType", refType, "refId", refId, "filename", file.getFilename()));
	
					// delete them if found
					for (Map<String, Object> existingFile : existingFiles) {
						fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"),
								(String) existingFile.get("refType"), (String) existingFile.get("skey"));
					}
				}
	
				// create UserFile
				file.setSkey(RandomStringUtils.randomAlphanumeric(16));
				fileService.saveFile(file);
	
				// create UserFileBlob
				fileBlob.setFileId(file.getId());
				fileService.saveFileBlob(fileBlob);
	
				// create UserFileRef
				fileRef.setFileId(file.getId());
				fileService.saveFileRef(fileRef);
	
				if(refType.equals(FileRefType.ORDER)){
					orderService.log(LogUtils.ACTION_CREATE,
						refId, 
						MapUtils.toHashMap("photoFileId", file.getId(), "filename",file.getFilename()));
				}
				logger.info("file done : "+ multipartFile.getName());

			}else{
				model.addAttribute(Params.SUCCESS, false);
				return JsonView.NAME;
			}

		}
		
		model.addAttribute(Params.SUCCESS, true);

		return JsonView.NAME;
	}

	@RequestMapping(value = "/measurement-sheet/ul", method = RequestMethod.POST)
	public String uploadMeasurementSheetFile(HttpServletRequest request, Model model,
			@RequestParam int orderId, @RequestParam MultipartFile multipartFile) throws Exception {

		Order order = orderService.find(orderId);
		String refType = FileRefType.MEASUREMENT_SHEET;
		String refCode = order.getCustId() + "";
		Integer refId = orderId;

		// get file upload max file size setting
		int uploadMaxFileSize = settingsService.getInt("FILE.upload.maxFileSize", DEFAULT_UPLOAD_MAX_FILE_SIZE_MB) * 1024 * 1024;

		Boolean success = Boolean.TRUE;

		// only proceed if multipartFile is not null, and has file size
		if (multipartFile != null && multipartFile.getSize() > 0 && multipartFile.getSize() <= uploadMaxFileSize) {

			// DEBUG LOG
			logger.info("multipartFile.getSize() = " + multipartFile.getSize());

			File file = new File();
			file.setFilename(multipartFile.getOriginalFilename());
			// file.setMimetype(multipartFile.getContentType());
			file.setMimetype(FileUtils.guessMimetype(file.getFilename()));
			file.setFilesize(multipartFile.getSize());
			file.setDescription(ServletRequestUtils.getTrimmedStringParameter(request, "description"));

			FileBlob fileBlob = new FileBlob();
			fileBlob.setBytes(multipartFile.getBytes());

			FileRef fileRef = new FileRef();
			fileRef.setRefId(refId);
			fileRef.setRefType(refType);
			fileRef.setRefCode(refCode);

			// get height and width if mimetype is png or jpeg
			if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) {
				BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes()));
				if (image != null) {
					file.setImageHeight(image.getHeight());
					file.setImageWidth(image.getWidth());
				}
			}

			if (OVERWRITE_SAME_FILENAME) {
				// search for existing file(s) with the same refType, refId, and filename
				List<Map<String, Object>> existingFiles = fileService.searchFiles(
						MapUtils.toHashMap("refType", refType, "refId", refId, "filename", file.getFilename()));

				// delete them if found
				for (Map<String, Object> existingFile : existingFiles) {
					fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"),
							(String) existingFile.get("refType"), (String) existingFile.get("skey"));
				}
			}

			// create UserFile
			file.setSkey(RandomStringUtils.randomAlphanumeric(16));
			fileService.saveFile(file);

			// create UserFileBlob
			fileBlob.setFileId(file.getId());
			fileService.saveFileBlob(fileBlob);

			// create UserFileRef
			fileRef.setFileId(file.getId());
			fileService.saveFileRef(fileRef);

			order.setFileId(file.getId());
			orderService.saveOrUpdate(order);
			orderService.log(LogUtils.ACTION_CREATE,
									order.getId(), 
									MapUtils.toHashMap("fileId", order.getFileId(), "filename", file.getFilename()));
		} else {
			success = Boolean.FALSE;

			// if not success, return msg to client
			model.addAttribute(Params.MSG, getMessageSourceAccessor().getMessage("Upload Failed"));
		}

		model.addAttribute(Params.SUCCESS, success);

		return JsonView.NAME;
	}

	@RequestMapping(value = "/band/icon/ul", method =  { RequestMethod.GET, RequestMethod.POST })
	public String uploadBandIconFile(HttpServletRequest request, Model model,
			@RequestParam int bandId, @RequestParam MultipartFile multipartFile) throws Exception {

		MaterialBand band = materialService.findBand(bandId);
		String refType = FileRefType.BAND_ICON;
		Integer refId = bandId;

		// get file upload max file size setting
		int uploadMaxFileSize = settingsService.getInt("FILE.upload.maxFileSize", DEFAULT_UPLOAD_MAX_FILE_SIZE_MB) * 1024 * 1024;

		Boolean success = Boolean.TRUE;

		// only proceed if multipartFile is not null, and has file size
		if (multipartFile != null && multipartFile.getSize() > 0 && multipartFile.getSize() <= uploadMaxFileSize) {

			// DEBUG LOG
			logger.info("multipartFile.getSize() = " + multipartFile.getSize());

			File file = new File();
			file.setFilename(multipartFile.getOriginalFilename());
			// file.setMimetype(multipartFile.getContentType());
			file.setMimetype(FileUtils.guessMimetype(file.getFilename()));
			file.setFilesize(multipartFile.getSize());
			file.setDescription(ServletRequestUtils.getTrimmedStringParameter(request, "description"));

			FileBlob fileBlob = new FileBlob();
			fileBlob.setBytes(multipartFile.getBytes());

			FileRef fileRef = new FileRef();
			fileRef.setRefId(refId);
			fileRef.setRefType(refType);

			// get height and width if mimetype is png or jpeg
			if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) {
				BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes()));
				if (image != null) {
					file.setImageHeight(image.getHeight());
					file.setImageWidth(image.getWidth());
				}
			}


			// search for existing file(s) with the same refType, refId, and filename
			List<Map<String, Object>> existingFiles = fileService.searchFiles(
					MapUtils.toHashMap("refType", refType, "refId", refId));

			// delete them if found
			for (Map<String, Object> existingFile : existingFiles) {
				fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"),
						(String) existingFile.get("refType"), (String) existingFile.get("skey"));
			}
	

			// create UserFile
			file.setSkey(RandomStringUtils.randomAlphanumeric(16));
			fileService.saveFile(file);

			// create UserFileBlob
			fileBlob.setFileId(file.getId());
			fileService.saveFileBlob(fileBlob);

			// create UserFileRef
			fileRef.setFileId(file.getId());
			fileService.saveFileRef(fileRef);

			band.setFileId(file.getId());
			materialService.saveOrUpdateBand(band);
		} else {
			success = Boolean.FALSE;

			// if not success, return msg to client
			model.addAttribute(Params.MSG, getMessageSourceAccessor().getMessage("Upload Failed"));
		}

		model.addAttribute(Params.SUCCESS, success);

		return JsonView.NAME;
	}

	@RequestMapping(value = "/load-image", method =  { RequestMethod.GET, RequestMethod.POST })
	private String loadImage(){

		List<Map<String, Object>> custList = fileService.getCustImageList();
		List<Map<String, Object>> orderList = fileService.getOrderImageList();

		String ignored = "";

		for(Map<String, Object> item: custList ){
			int custId = NumberUtils.intValue(item.get("id"), 0);
			if(custId == 0) {
				ignored = ignored+"\n CustId:"+item.get("id")+", photo:"+item.get("cust_photo");
				continue;
			}
			
			String[] photos = item.get("cust_photo").toString().trim().split("::");
			for(String filename: photos){
				String path = this.getClass().getClassLoader().getResource("").getPath() + "../report/"+filename;
				if(path != null){
					java.io.File file = new java.io.File(path);
					try {
						createFile(custId, FileRefType.CUSTOMER, file, filename);
					} catch (IOException e) {
						ignored = ignored+"\n CustId:"+item.get("id")+", photo:"+filename;
					}
				}
			}
		}

		for(Map<String, Object> item: orderList){
			int orderId = NumberUtils.intValue(item.get("id"), 0);
			if(orderId == 0) {
				ignored = ignored+"\n OrderId:"+item.get("id")+", photo:"+item.get("order_photo");
				continue;
			}

			String[] ophotos = item.get("order_photo").toString().trim().split("::");
			for(String filename: ophotos){
				String path = this.getClass().getClassLoader().getResource("").getPath() + "../report/"+filename;
				if(path != null){
					java.io.File file = new java.io.File(path);
					try {
						createFile(orderId, FileRefType.ORDER, file, filename);
					} catch (IOException e) {
						ignored = ignored+"\n OrderId:"+item.get("id")+", photo:"+filename;
					}
				}
			}
		}

		logger.info(ignored);

		return JsonView.NAME;
	}

	private void createFile(int refId, String refType, java.io.File f, String filename) throws IOException{
		byte[] fileContent = Files.readAllBytes(f.toPath());

		File file = new File();
		file.setFilename(filename);
		// file.setMimetype(multipartFile.getContentType());
		file.setMimetype(FileUtils.guessMimetype(filename));
		file.setFilesize(fileContent.length);

		FileBlob fileBlob = new FileBlob();
		fileBlob.setBytes(fileContent);

		FileRef fileRef = new FileRef();
		fileRef.setRefId(refId);
		fileRef.setRefType(refType);

		// get height and width if mimetype is png or jpeg
		if (AbstractView.CONTENT_TYPE_PNG.equals(file.getMimetype()) || AbstractView.CONTENT_TYPE_JPEG.equals(file.getMimetype())) {
			BufferedImage image = ImageIO.read(new ByteArrayInputStream(fileBlob.getBytes()));
			if (image != null) {
				file.setImageHeight(image.getHeight());
				file.setImageWidth(image.getWidth());
			}
		}

		// search for existing file(s) with the same refType, refId, and filename
		List<Map<String, Object>> existingFiles = fileService.searchFiles(
				MapUtils.toHashMap("refType", refType, "refId", refId));

		// delete them if found
		for (Map<String, Object> existingFile : existingFiles) {
			fileService.deleteFile((Integer) existingFile.get("id"), (Integer) existingFile.get("refId"),
					(String) existingFile.get("refType"), (String) existingFile.get("skey"));
		}

		// create UserFile
		file.setSkey(RandomStringUtils.randomAlphanumeric(16));
		fileService.saveFile(file);

		// create UserFileBlob
		fileBlob.setFileId(file.getId());
		fileService.saveFileBlob(fileBlob);

		// create UserFileRef
		fileRef.setFileId(file.getId());
		fileService.saveFileRef(fileRef);
	}


}
