package com.happycxz.controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.NoSuchAlgorithmException;
import java.util.Map;  
  
import javax.servlet.ServletException;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.Part;

import org.springframework.stereotype.Controller;  
import org.springframework.util.StringUtils;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RequestParam;  
import org.springframework.web.bind.annotation.ResponseBody;

import com.happycxz.olami.AsrAdditionInfo;
import com.happycxz.olami.OlamiEntityFactory;
import com.happycxz.olami.SdkEntity;
import com.happycxz.utils.Configuration;
import com.happycxz.utils.Util;
import com.sun.org.apache.xml.internal.security.utils.Base64;  

/** 
 * olami与微信小程序 接口相关对接
 * @author Jod
 */
@Controller  
@RequestMapping("/olami")  
public class OlamiController {
	
	//保存linux shell命令字符串
	private static final String SHELL_CMD = Configuration.getInstance().getValue("local.shell.cmd", "sh /YOUR_PATH/silk-v3-decoder/converter_cxz.sh %s wav");

    //保存silk_v3, mp3和wav文件的目录,放在web目录、或一个指定的绝对目录下 
    private static final String localFilePath = Configuration.getInstance().getValue("local.file.path", "/YOUR/LOCAL/VOICE/PATH/");;  
    
    static {
    	Util.p("OlamiController base SHELL_CMD:" + SHELL_CMD);
    	Util.p("OlamiController base localFilePath:" + localFilePath);
    }

    @RequestMapping(value="/asr", produces="plain/text; charset=UTF-8")  
    public @ResponseBody String asrUploadFile(HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> p)  
            throws ServletException, IOException {  
    	return processBase(request, p, false);
    }  
   
    @RequestMapping(value="/mp3asr", produces="plain/text; charset=UTF-8")  
    public @ResponseBody String asrUploadFileMp3(HttpServletRequest request, HttpServletResponse response, @RequestParam Map<String, Object> p)  
            throws ServletException, IOException {
    	return processBase(request, p, true);
    }
    
    public String processBase(HttpServletRequest request, @RequestParam Map<String, Object> p, boolean isMp3)  
            throws ServletException, IOException {  

    	AsrAdditionInfo additionInfo = new AsrAdditionInfo(p);
    	if (additionInfo.getErrCode() != 0) {
    		//参数不合法,或者appKey没有在支持列表中备录
    		return Util.JsonResult(String.valueOf(additionInfo.getErrCode()), additionInfo.getErrMsg());  
    	}
    	
    	String localPathToday = localFilePath + getSrcFmt(isMp3) + File.separator + Util.getDateStr() + File.separator;
        // 如果文件存放路径不存在,则mkdir一个  
        File fileSaveDir = new File(localPathToday);  
        if (!fileSaveDir.exists()) {  
            fileSaveDir.mkdirs();  
        }
  
        int count = 1;
        String asrResult = "";
        for (Part part : request.getParts()) {  
            String fileName_origin = extractFileName(part);
            //这里必须要用原始文件名是否为空来判断,因为part列表是所有数据,前三个被formdata占了,对应文件名其实是空
            if(!StringUtils.isEmpty(fileName_origin)) {
            	Util.p("originFileName[" + count + "]:" + fileName_origin);
            	String fileName = additionInfo.getVoiceFileName(isMp3);
            	//DEBUG on windows, add temp path preffix to local D: to preserve part.write exception.
            	//String recFile = "D:" + localPathToday + fileName;
            	String recFile = localPathToday + fileName;
            	Util.p("recFileName[" + count + "]:" + recFile);

            	part.write(recFile);

            	if (webmBase64Decode2Wav(recFile)) {
            		//support webm/base64 in webmBase64Decode2Wav(), wxapp develop IDE record format. 
            		//even if the suffix is xx.silk(wx.startRecord generate) or xx.mp3(wx.getRecorderManager generate)
            		//if webm base64 format , and xxxx.webm file is temporary created, xxxx.wav was last be converted.
            	} else {
            		// run script to convert silk_v3 or mp3 to wav
                    Util.RunShell2Wav(SHELL_CMD, recFile);
            	}
            	
                // get wave file path and name, prepare for olami asr
                String waveFile = DotMp3OrDotSilk2DotOther(recFile, "wav");
                Util.p("OlamiController.asrUploadFile() waveFile:" + waveFile);
                
                if (new File(waveFile).exists() == false) {
                	Util.w("OlamiController.asrUploadFile() wav file[" + waveFile + "] not exist!", null);
					return Util.JsonResult("80", "convert " + getSrcFmt(isMp3) + " to wav failed, NOW NOT SUPPORT WXAPP DEVELOP RECORD because it is not " + getSrcFmt(isMp3) + " format. anyother reason please tell QQ:404499164."); 
                }
                
                try {
                	SdkEntity entity = OlamiEntityFactory.createEntity(additionInfo.getAppKey(), additionInfo.getAppSecret(), additionInfo.getUserId());
					asrResult = entity.getSpeechResult(waveFile);
					Util.p("OlamiController.asrUploadFile() asrResult:" + asrResult);
				} catch (NoSuchAlgorithmException | InterruptedException e) {
					Util.w("OlamiController.asrUploadFile() asr NoSuchAlgorithmException or InterruptedException", e);
				} catch (FileNotFoundException e) {
					Util.w("OlamiController.asrUploadFile() asr FileNotFoundException", e);
					return Util.JsonResult("80", "convert " + getSrcFmt(isMp3) + " to wav failed, NOW NOT SUPPORT WXAPP DEVELOP RECORD because it is not " + getSrcFmt(isMp3) + " format. anyother reason please tell QQ:404499164."); 
				} catch (Exception e) {
					Util.w("OlamiController.asrUploadFile() asr Exception", e);
				}
            }
            count++;
        }
        
        //防止数据传递乱码
        //response.setContentType("application/json;charset=UTF-8");

        return Util.JsonResult("0", "olami asr success!", asrResult);  
    }
    
    private static String getSrcFmt(boolean isMp3) {
    	return (isMp3 ? "mp3":"silk_v3");
    }
    
    /**
     * 将  xxxxx.silk 文件名转 xxxx.wav
     * @param silkName
     * @param otherSubFix
     * @return
     */
    private static String DotMp3OrDotSilk2DotOther(String recName, String otherSubFix) {
    	int removeByte = 4;
    	if (recName.endsWith("silk")) {
    		removeByte = 4;
    	} else if (recName.endsWith("slk") || recName.endsWith("mp3")) {
    		removeByte = 3;
    	}
    	return recName.substring(0, recName.length()-removeByte) + otherSubFix;
    }
    
    /** 
     * 从content-disposition头中获取源文件名 
     *  
     * content-disposition头的格式如下: 
     * form-data; name="dataFile"; filename="PHOTO.JPG" 
     *  
     * @param part 
     * @return 
     */
	private String extractFileName(Part part) {  
        String contentDisp = part.getHeader("content-disposition");  
        String[] items = contentDisp.split(";");  
        for (String s : items) {  
            if (s.trim().startsWith("filename")) {  
                return s.substring(s.indexOf("=") + 2, s.length()-1);  
            }  
        }  
        return "";  
    }


    /**
     * 通过filePath内容判断是否是webm/base64格式,如果是,先decode base64后,再直接ffmpeg转wav,
     * 如果不是,返回false丢给外层继续当作silk v3去解
     * @param filePath
     * @return
     */
	public static boolean webmBase64Decode2Wav(String filePath) {
		boolean isWebm = false;
		try {
			String encoding = "utf-8";
			File file = new File(filePath);
			// 判断文件是否存在
			if ((file.isFile() == false) || (file.exists() == false)) {
				Util.w("webmBase64Decode2Wav() no file[" + filePath + "] exist.", null);
			}
			
			StringBuilder lineTxt = new StringBuilder();
			String line = null;
			try (
			InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);
			BufferedReader bufferedReader = new BufferedReader(read);) {
				while ((line = bufferedReader.readLine()) != null) {
					lineTxt.append(line);
				}
				read.close();
			} catch (Exception e) {
				Util.w("webmBase64Decode2Wav() exception0:", e);
				return isWebm;
			}
			
			String oldData = lineTxt.toString();
			if (oldData.startsWith("data:audio/webm;base64,") == false) {
				Util.d("webmBase64Decode2Wav() file[" + filePath + "] is not webm, or already decoded." );
				return isWebm;
			}
			
			isWebm = true;
			oldData = oldData.replace("data:audio/webm;base64,", "");
			String webmFileName = DotMp3OrDotSilk2DotOther(filePath, "webm");
			try {

				File webmFile = new File(webmFileName);
				byte[] bt = Base64.decode(oldData);
				FileOutputStream in = new FileOutputStream(webmFile);
				try {
					in.write(bt, 0, bt.length);
					in.close();
				} catch (IOException e) {
					Util.w("webmBase64Decode2Wav() exception1:", e);
					return isWebm;
				}
			} catch (FileNotFoundException e) {
				Util.w("webmBase64Decode2Wav() exception2:", e);
				return isWebm;
			}
			
			// run cmd to convert webm to wav
    		Util.RunShell2Wav(SHELL_CMD, webmFileName);
		} catch (Exception e) {
			Util.w("webmBase64Decode2Wav() exception3:", e);
			return isWebm;
		}
		
		return isWebm;
	}
	
	public static void main(String[] args) {
		webmBase64Decode2Wav("D:\\secureCRT_RZSZ\\1505716415538_f7d98081-4d21-3b40-a7df-e56c046a784d_b4118cd178064b45b7c8f1242bcde31f.silk");
	}
}