package com.goafanti.admin.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Calendar;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import net.bramp.ffmpeg.FFmpeg;
import net.bramp.ffmpeg.FFmpegExecutor;
import net.bramp.ffmpeg.FFmpegUtils;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.builder.FFmpegBuilder;
import net.bramp.ffmpeg.job.FFmpegJob;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import net.bramp.ffmpeg.progress.Progress;
import net.bramp.ffmpeg.progress.ProgressListener;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.goafanti.admin.service.AdminVideoService;
import com.goafanti.common.bo.Result;
import com.goafanti.common.constant.ErrorConstants;
import com.goafanti.common.controller.CertifyApiController;
import com.goafanti.common.enums.AttachmentType;
import com.goafanti.common.enums.VideoFields;
import com.goafanti.common.model.JtVideo;
import com.goafanti.common.utils.StringUtils;
import com.goafanti.core.shiro.token.TokenManager;

@Controller
@RequestMapping(value = "/api/admin/video")
public class AdminVideoApiController extends CertifyApiController {

	@Resource
	private AdminVideoService adminVideoService;

	@Value(value = "${ffmpeg.path}")
	private String ffmpegPath = null;
	@Value(value = "${ffprobe.path}")
	private String ffprobePath = null;
	@Value(value = "${video.temppath}")
	private String videoTemppath = null;
	
	

	// 上传视频只做MP4格式的
	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	@ResponseBody
	public Result uploadPicture(HttpServletRequest req, String sign) {
		Result res = new Result();
		AttachmentType attachmentType = AttachmentType.getField(sign);
		if (attachmentType == AttachmentType.VIDEO) {
			// 先将视频存在本地文件中
			String dir =  videoTemppath + "/convert_before";
			String filename = handleVideoFiles(res, req, dir);
			res.setData(filename);
		} else if (attachmentType == AttachmentType.VIDEO_COVER) {
			String picturebase = req.getParameter("picturebase");
			String filename = req.getParameter("filename");
			byte[] bs = Base64.getDecoder().decode(picturebase);
			filename = System.nanoTime()
					+ filename.substring(filename.indexOf("."));
			res.setData(handleBaseFiles(res, "/video_cover/", false, bs, sign,
					TokenManager.getUserId(), filename));

		} else {
			res.getError().add(
					buildError(ErrorConstants.PARAM_ERROR, "", "文件标示"));
		}
		return res;
	}

	private String createFileName() {
		// 年月日
		Calendar now = Calendar.getInstance();
		int year = now.get(Calendar.YEAR);
		int month = now.get(Calendar.MONTH) + 1;
		int day = now.get(Calendar.DAY_OF_MONTH);
		String dir = "/" + year + "/" + month + "/" + day;
		return dir;
	}

	// 查看所有视频信息
	@RequestMapping(value = "/getVideoList", method = RequestMethod.GET)
	@ResponseBody
	public Result getVideoList(JtVideo video, Integer pageNo, Integer pageSize) {
		Result result = new Result();
		result.setData(adminVideoService.getVideoList(video, pageNo, pageSize,1));
		return result;
	}

	// 查看单个信息
	@RequestMapping(value = "/getVideoById", method = RequestMethod.GET)
	@ResponseBody
	public Result getVideoById(String id) {
		Result result = new Result();
		JtVideo video = new JtVideo();
		video.setId(id);
		result.setData(adminVideoService.getVideoById(video));
		return result;
	}

	// 新增视频信息
	@RequestMapping(value = "/insertVideo", method = RequestMethod.POST)
	@ResponseBody
	public Result insertVideo(@Valid JtVideo video , BindingResult bindingResult) throws IOException,
			InterruptedException, ExecutionException {
		// 数据限制
		Result result = new Result();
		if (bindingResult.hasErrors()) {
			result.getError().add(buildErrorByMsg(bindingResult.getFieldError().getDefaultMessage(),
					VideoFields.getFieldDesc(bindingResult.getFieldError().getField())));
			return result;
		}
		if(StringUtils.isBlank(video.getName())){
			result.getError().add(
					buildError( "视频名称不能是空", "视频名称不能是空"));
			return result;
		}
		//名称重名限制
		if(adminVideoService.nameCheck(video.getName().trim()) > 0){
			result.getError().add(
					buildError(ErrorConstants.PARAM_ERROR, "", "视频名称重复不能新增"));
			return result;
		}
		
		String filename = video.getUrl();
		
		// 生成保存到数据库的url
		String output = System.nanoTime() + ".mp4";
		
		String filepath = createFileName();

		video.setUrl(filepath + "/" + output);
		
		video.setTranscoding(1);
		// 保存到数据库
		result.setData(adminVideoService.insertVideo(video));

		// 压缩视频并将视频存储
		
		FileChannel fc= null;  
	    try {  
	        File f= new File(filename);  
	        if (f.exists() && f.isFile()){  
	            FileInputStream fis= new FileInputStream(f);  
	            fc= fis.getChannel();  
	            Long fsize = fc.size()/1024/1024;
	            System.out.println(fc.size());
	            if(fsize.longValue() > 50){
	            	videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after" + filepath );
	            }else{
	            	copy(filename,  output, videoTemppath + "/convert_after" + filepath + "/" );
	            	//修改数据库
	            	updateSql(video.getUrl(), videoTemppath + "/convert_after" + filepath ,output);
	            }
	            
	            
	        }else{  
	        	System.out.println("file doesn't exist or is not a file");  
	        }  
	    } catch (FileNotFoundException e) {  
	    	System.out.println(e);  
	    } catch (IOException e) {  
	    	System.out.println(e);  
	    } finally {  
	        if (null!=fc){  
	            try{  
	                fc.close();  
	            }catch(IOException e){  
	            	System.out.println(e);  
	            }  
	        }   
	    }  
		
		
		
		return result;
	}

	// 修改视频
	@RequestMapping(value = "/updateVideo", method = RequestMethod.POST)
	@ResponseBody
	public Result updateVideo(@Valid JtVideo video , BindingResult bindingResult) throws IOException,
			InterruptedException, ExecutionException {
		Result result = new Result();
		if (bindingResult.hasErrors()) {
			result.getError().add(buildErrorByMsg(bindingResult.getFieldError().getDefaultMessage(),
					VideoFields.getFieldDesc(bindingResult.getFieldError().getField())));
			return result;
		}
		JtVideo v = new JtVideo();
		v.setId(video.getId());
		JtVideo oldVideo = adminVideoService.getVideoById(v);
		if (StringUtils.isBlank(video.getId())) {
			result.getError().add(
					buildError(ErrorConstants.PARAM_ERROR, "", "视频id不能为空"));
			return result;
		} else if (null == oldVideo) {
			result.getError().add(
					buildError(ErrorConstants.PARAM_ERROR, "", "视频id不存在"));
			return result;
		}
		if(null != video.getName() && "".equals(video.getName().trim())){
			result.getError().add(
					buildError( "视频名称不能是空", "视频名称不能是空"));
			return result;
		}
		//如果是删除视频
		if(null != video.getStatus() && video.getStatus() == 3){
			if(oldVideo.getStatus() == 1){
				result.getError().add(
						buildError( "视频已经发布不能删除", "视频已经发布不能删除"));
				return result;
			}else{
				deleteFtpVideo( videoTemppath + "/convert_after" + oldVideo.getUrl());
			}
		}
		Boolean flag = false;
		String filepath = "";
		String output = "";
		if (StringUtils.isNotBlank(video.getUrl()) && !video.getUrl().equals(oldVideo.getUrl())) { // 如果修改了视频
			video.setTranscoding(1);
			// 压缩视频并将视频
			String filename = video.getUrl();
			output = System.nanoTime() + ".mp4";
			filepath = createFileName() ;
			video.setUrl(filepath + "/" + output);
			FileChannel fc= null;  
		    try {  
		        File f= new File(filename);  
		        if (f.exists() && f.isFile()){  
		            FileInputStream fis= new FileInputStream(f);  
		            fc= fis.getChannel();  
		            Long fsize = fc.size()/1024/1024;
		            System.out.println(fc.size());
		            if(fsize.longValue() > 50){
		            	videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after" + filepath );
		            }else{
		            	copy(filename,  output, videoTemppath + "/convert_after" + filepath + "/" );
		            	flag = true;
		            }
		        }else{  
		        	System.out.println("file doesn't exist or is not a file");  
		        }  
		    } catch (FileNotFoundException e) {  
		    	System.out.println(e);  
		    } catch (IOException e) {  
		    	System.out.println(e);  
		    } finally {  
		        if (null!=fc){  
		            try{  
		                fc.close();  
		            }catch(IOException e){  
		            	System.out.println(e);  
		            }  
		        }   
		    }  
			// videoChange(filename, output, video.getUrl(), videoTemppath + "/convert_after" + filepath);
			
			//删除原来远程的视频
			deleteFtpVideo(videoTemppath + "/convert_after" + oldVideo.getUrl());
		}else{
			video.setUrl(null);
		}

		result.setData(adminVideoService.updateVideo(video));
		if(flag){
			updateSql(video.getUrl(), videoTemppath + "/convert_after" + filepath ,output);
		}
		return result;
	}
	
	private Boolean deleteFtpVideo(String url){
		boolean b = false;
		//判断文件是否存在
		File f= new File(url);
		if(f.exists()){
			System.out.println("+++++++++++++++++++++++++++++文件存在,删除视频"+url);
			b = f.delete();
		}
		return b;
	}

	// 将文件转码
	private String videoChange(String input, String output, String url,String outputdir) throws IOException,
			InterruptedException, ExecutionException {
		File file = new File(outputdir);
		if(!file.isDirectory()){
			file.mkdirs();//创建文件夹
		}
		FFmpeg ffmpeg = new FFmpeg(ffmpegPath);
		FFprobe ffprobe = new FFprobe(ffprobePath);

		FFmpegProbeResult in = ffprobe.probe(input);

		FFmpegBuilder builder = new FFmpegBuilder();
		builder.setInput(in) // 输入文件
				.overrideOutputFiles(true) // 覆盖重复文件
				.addOutput(outputdir + "/" + output) // 输出文件
				.setFormat("mp4") // 设置格式
				.setTargetSize(250_000) // 目标大小
				.disableSubtitle() // 没有子标题
				.setAudioChannels(1) // 声道
				.setAudioSampleRate(48_000) // 音频采样率
				.setAudioBitRate(32768) // 音频传输率
				.setVideoCodec("libx264") // 视频编解码器
				.setVideoFrameRate(24, 1) // 视频帧速率
				.setVideoResolution(640, 480) // 视频分辨率
				.setStrict(FFmpegBuilder.Strict.EXPERIMENTAL) // 严格形式
				.done();

		FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
		ExecutorService service = Executors.newCachedThreadPool();
		// 3.直接new一个FutureTask
		SubTask subTask = new SubTask(executor, builder, in, url, input, output, outputdir);
		FutureTask<Boolean> result = new FutureTask<Boolean>(subTask);
		// 4.提交任务
		service.submit(result);
		// 5.关闭线程池
		service.shutdown();
		// LOGGER.info("=============返回给前端=============");
		
		//System.out.println("task运行结果" +  result.get());
		return "SUCESS";
	}

	private void updateSql(String url, String tempdir, String filename) {
		// 修改数据
		File file = new File(tempdir+"/"+filename);
		JtVideo video = new JtVideo();
		video.setMd5(FileMD5(file));
		video.setTranscoding(2);
		video.setUrl(url);
		url = url.substring(0,url.lastIndexOf("/"));
		System.out.println("修改数据库000000000000000000000"+url);
		adminVideoService.updateByUrl(video);//修改状态
	}
	
	// 获得文件md5
	private String FileMD5(File file) {
		try {
			FileInputStream fis = new FileInputStream(file);
			MessageDigest md = MessageDigest.getInstance("MD5");
			byte[] buffer = new byte[1024];
			int length = -1;
			while ((length = fis.read(buffer, 0, 1024)) != -1) {
				md.update(buffer, 0, length);
			}
			BigInteger bigInt = new BigInteger(1, md.digest());
			return bigInt.toString(16);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	class SubTask implements Callable<Boolean> {
		private Boolean state = false;
		private FFmpegExecutor executor;
		private FFmpegBuilder builder;
		private FFmpegProbeResult probeResult;
		private String url;
		private String input;
		private String output;
		private String tempdir;

		public SubTask(FFmpegExecutor executor, FFmpegBuilder builder,
				FFmpegProbeResult probeResult, String url, String input,
				String output, String tempdir) {
			this.executor = executor;
			this.builder = builder;
			this.probeResult = probeResult;
			this.url = url;
			this.input = input;
			this.output = output;
			this.tempdir = tempdir;
		}

		public FFmpegExecutor getExecutor() {
			return executor;
		}

		public FFmpegBuilder getBuilder() {
			return builder;
		}

		public String getUrl() {
			return url;
		}

		public String getInput() {
			return input;
		}

		public String getOutput() {
			return output;
		}

		public Boolean getState() {
			return state;
		}

		@Override
		public Boolean call() throws Exception {
			final double duration_ns = probeResult.getFormat().duration
					* TimeUnit.SECONDS.toNanos(1);
			try {
				FFmpegJob job = executor.createJob(builder,
						new ProgressListener() {
							@Override
							public void progress(Progress progress) {
								double percentage = progress.out_time_ns
										/ duration_ns;
								System.out.println(String
										.format("[%.0f%%] status:%s frame:%d time:%s ms fps:%.0f speed:%.2fx",
												percentage * 100,
												progress.status,
												progress.frame,
												FFmpegUtils.toTimecode(
														progress.out_time_ns,
														TimeUnit.NANOSECONDS),
												progress.fps.doubleValue(),
												progress.speed));
							}
						});
				job.run();
				// 6.阻塞 call
				// System.out.println("设置 sate");
				// latch.wait();
				state = true;
				updateSql(url, tempdir,output);
				// 删除未压缩的视频
				File f1 = new File(input);
				f1.delete();
			} catch (Exception e) {
				e.printStackTrace();
			}
			return state;
		}
	}
	
	public static void copy(String file1, String file2, String dir) {

		File file = new File(dir);
		if(!file.isDirectory()){
			file.mkdirs();//创建文件夹
		}
		System.out.println(file1);
		System.out.println(file2);
		File src = new File(file1);
		InputStream in = null;
		OutputStream out = null;
		System.out.println(file1.substring(file1.lastIndexOf("/"),file1.length()));//获取单个文件的源文件的名称
		try {
			in = new BufferedInputStream(new FileInputStream(src), 16 * 1024);
			FileOutputStream f = new FileOutputStream(dir+file2);// 一定要加上文件名称
			out = new BufferedOutputStream(f, 16 * 1024);
			byte[] buffer = new byte[16 * 1024];
			int len = 0;
			while ((len = in.read(buffer)) > 0) {
				out.write(buffer, 0, len);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (null != in) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != out) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	
	public static void main(String[] args) {
		copy("D:/Downloads/环保小视频.mp4","haha.mp4","F:/data/local/video/convert_after/2018/11/9/");
	}

}
