package com.zhili.stationcontrol.tts.component;

import com.alibaba.fastjson.JSON;
import com.zhili.stationcontrol.tts.util.WavPlayUtil;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author ：HuangBin
 * @description：TODO
 * @date ：2023/5/17 19:00
 */
@Component
@Slf4j
public class XunFeiTextToSpeech implements DisposableBean, Runnable {

    Thread thread;

    public static String loginParams;

    public static String sessionBeginParams;

    public static String filename;

    public static String linkFile;

    public static String playWay;
    @Autowired
    RedisTemplate redisTemplate;

    @Value("${xunfei_tts.play_way}")
    public void setPlayWay(String p) {
        playWay = p;
    }

    @Value("${xunfei_tts.login_params}")
    public void setLoginParams(String p) {
        loginParams = p;
    }

    @Value("${xunfei_tts.session_begin_params}")
    public void setSessionBeginParams(String p) {
        sessionBeginParams = p;
    }

    @Value("${xunfei_tts.filename}")
    public void setFilename(String p) {
        filename = p;
    }


    @Value("${xunfei_tts.link_file}")
    public void setLinkFile(String p) {
        linkFile = p;
    }

    @PostConstruct
    public void init() {
        log.info("login_params: {}", loginParams);
        //登录参数,appid与msc库绑定,请勿随意改动
        int loginCode = MscLibrary.INSTANCE.MSPLogin(null, null, loginParams);
        log.info("loginCode: {}", loginCode);
        if (loginCode != 0) {
            //登录失败
            MscLibrary.INSTANCE.MSPLogout();
            log.info("loginfail:登录失败");
            return;
        }

        this.thread = new Thread(this);
        this.thread.start();
        log.info("start:线程启动");
    }

    @Override
    public void run() {
        while (true) {
            RandomAccessFile raf = null;
            String sessionId = null;
            try {
                Object voiceObj = redisTemplate.opsForList().leftPop("ttsList", 500l, TimeUnit.MILLISECONDS);
                if (voiceObj == null) {
                    continue;
                }
                Map<String, Object> voice = JSON.parseObject(voiceObj.toString(), Map.class);
                String text = voice.get("txt").toString();
                Integer vol = Integer.valueOf(voice.get("vol").toString());
                log.info("speak:{},vol:{}", text, vol);
                IntByReference errCode = new IntByReference();
                sessionId = MscLibrary.INSTANCE.QTTSSessionBegin(sessionBeginParams, errCode);
                if (errCode.getValue() != 0) {
                    log.error("会话失败:" + errCode.getValue());
                    throw new RuntimeException("会话失败");
                }
                //放入文本
                int textPutCode = MscLibrary.INSTANCE.QTTSTextPut(sessionId, text, text.getBytes().length, null);
                if (textPutCode != 0) {
                    log.error("放入文本失败:" + textPutCode);
                    throw new RuntimeException("放入文本失败");
                }
                raf = new RandomAccessFile(this.filename, "rw");
                raf.write(new byte[44]);
                int dataSize = 0;
                IntByReference audioLen = new IntByReference();
                IntByReference synthStatus = new IntByReference();
                while (true) {
                    Pointer pointer = MscLibrary.INSTANCE.QTTSAudioGet(sessionId, audioLen, synthStatus, errCode);
                    if (pointer != null && audioLen.getValue() > 0) {
                        //写入合成内容
                        raf.write(pointer.getByteArray(0, audioLen.getValue()));
                        //记录数据长度
                        dataSize += audioLen.getValue();
                    }
                    //转换异常或转换结束跳出循环
//                    log.error("errCode.getValue():{}", errCode.getValue());
                    if (errCode.getValue() != 0 || synthStatus.getValue() == 2) {
                        break;
                    }
                }
                if (textPutCode != 0) {
                    //获取转换数据失败
                    log.error("获取转换数据失败");
                    throw new RuntimeException("获取转换数据失败");
                }
                //定位到文件起始位置
                raf.seek(0);
                //写入真实头格式
                raf.write(getWavHeader(dataSize, 16000, 32000, 1, 16));
                if (playWay.equals("java")) {
                    WavPlayUtil.play(filename);
                } else if (playWay.equals("sox")) {
                    WavPlayUtil.play2(filename, vol);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (sessionId != null) {
                    MscLibrary.INSTANCE.QTTSSessionEnd(sessionId, "Normal");
                }
                if (raf != null) {
                    try {
                        raf.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    @Override
    public void destroy() throws Exception {
        MscLibrary.INSTANCE.MSPLogout();
    }


    public interface MscLibrary extends Library {

        // DLL文件默认路径为项目根目录，若DLL文件存放在项目外，请使用绝对路径
        MscLibrary INSTANCE = Native.load("lib/" + XunFeiTextToSpeech.linkFile, MscLibrary.class);

        int MSPLogin(String username, String password, String param);

        int MSPLogout();

        String QTTSSessionBegin(String params, IntByReference errorCode);

        int QTTSTextPut(String sessionID, String textString, int textLen, String params);

        Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);

        int QTTSSessionEnd(String sessionID, String hints);
    }

    static byte[] getWavHeader(int totalAudioLen, int sampleRate, int byteRate, int nChannels, int weikuan) {
        long totalDataLen = totalAudioLen + 36;
        byte[] header = new byte[44];
        header[0] = 'R'; // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataLen & 0xff);
        header[5] = (byte) ((totalDataLen >> 8) & 0xff);
        header[6] = (byte) ((totalDataLen >> 16) & 0xff);
        header[7] = (byte) ((totalDataLen >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';
        header[12] = 'f'; // 'fmt ' chunk
        header[13] = 'm';
        header[14] = 't';
        header[15] = ' ';
        header[16] = 16; // 4 bytes: size of 'fmt ' chunk
        header[17] = 0;
        header[18] = 0;
        header[19] = 0;
        header[20] = 1; // format = 1
        header[21] = 0;
        header[22] = (byte) (nChannels & 0xff);
        header[23] = (byte) ((nChannels >> 8) & 0xff);

        header[24] = (byte) (sampleRate & 0xff);//采样率
        header[25] = (byte) ((sampleRate >> 8) & 0xff);
        header[26] = (byte) ((sampleRate >> 16) & 0xff);
        header[27] = (byte) ((sampleRate >> 24) & 0xff);

        header[28] = (byte) (byteRate & 0xff);//取八位
        header[29] = (byte) ((byteRate >> 8) & 0xff);
        header[30] = (byte) ((byteRate >> 16) & 0xff);
        header[31] = (byte) ((byteRate >> 24) & 0xff);

        int b = weikuan * nChannels / 8;//每次采样的大小
        header[32] = (byte) (b & 0xff); // block align
        header[33] = (byte) ((b >> 8) & 0xff);

        header[34] = (byte) (weikuan & 0xff);//位宽
        header[35] = (byte) ((weikuan >> 8) & 0xff);

        header[36] = 'd';//data
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (totalAudioLen & 0xff);
        header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
        header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
        header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
        return header;
    }
}
