package com.huawei.sis.demo.cn_demo;

import com.cloud.sdk.util.StringUtils;
import com.huawei.sis.bean.AuthInfo;
import com.huawei.sis.bean.SisConfig;
import com.huawei.sis.bean.SisConstant;
import com.huawei.sis.bean.VcsListener;
import com.huawei.sis.bean.request.VcsRequest;
import com.huawei.sis.bean.response.StateResponse;
import com.huawei.sis.bean.response.VcsDataResponse;
import com.huawei.sis.client.RvcsClient;
import com.huawei.sis.exception.SisException;
import com.huawei.sis.util.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 声音复刻流式接口Demo
 * <p>
 * Copyright 2021 Huawei Technologies Co.,Ltd.
 */
public class RvcsDemo {

    private static final Logger log = LoggerFactory.getLogger(RvcsDemo.class);

    // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；
    private String ak = System.getenv("HUAWEICLOUD_SDK_AK");
    private String sk = System.getenv("HUAWEICLOUD_SDK_SK");
    private String region = "";    // 区域，如cn-east-3
    private String projectId = ""; // 项目id，在我的凭证查看。参考https://support.huaweicloud.com/api-sis/sis_03_0008.html
    private String serviceEndPoint = "";  // 可选参数,传入new AuthInfo(ak, sk, region, projectId);
    private String audioPath = ""; // 待合成音频存储路径
    private String textPath = ""; // 可选 待合成文本存储路径,例如"D://text.txt",要求文本中，每行句子不超过300字符
    // 使用该脚本前,保证要使用的声音已经注册,voiceName为声音名称。若没有注册声音,请使用VcsRegisterAndRequest.java脚本，注册声音和查询声音
    private String voiceName = ""; // 音色名称，可以使用注册音色或预置音色

    public static void main(String[] args) {
        RvcsDemo rvcsDemo = new RvcsDemo();
        rvcsDemo.process();
    }

    private SisConfig getConfig() {
        SisConfig config = new SisConfig();
        // 设置连接超时，默认10000ms
        config.setConnectionTimeout(SisConstant.DEFAULT_CONNECTION_TIMEOUT);
        // 设置读取超时，默认10000ms
        config.setReadTimeout(SisConstant.DEFAULT_READ_TIMEOUT);
        // 设置代理, 一定要确保代理可用才启动此设置。 代理初始化也可用不加密的代理，new ProxyHostInfo(host, port);
        // 设置代理，可参考 config.setProxy(new ProxyHostInfo(host, port, user, password));
        return config;
    }

    private void setParameters(VcsRequest request) {
        // 1. 设置音频格式，默认pcm
        request.setAudioFormat("pcm");
        // 2. 设置采样率，24000、16000、8000，默认16000
        request.setSampleRate("16000");
        // 3. 设置音色，如 chinese_huaxiaoli_common、chinese_huaxiaotian_common
        request.setVoiceName(voiceName);
        // 4. 设置语速，取值范围为 -500~500，默认值为0
        request.setSpeed(0);
        // 5. 设置音调，取值范围为 -500~500，默认值为0
        request.setPitch(0);
        // 6. 设置音量，取值范围为 0~100，默认值为50
        request.setVolume(50);
        // 7. 设置模式，取值 ONLY 或 MULTI，默认为MULTI
        // ONLY模式下，用户发送一次文本，服务端流式返回语音数据；MULTI模式下，支持用户多次发送文本，服务端流式返回语音数据，可用于大模型输出实时播报等场景。
        request.setTextPieces("MULTI");
    }

    private static class MyVcsListener implements VcsListener {
        private String path;
        private FileOutputStream fos = null;
        private VcsListenerCallback vcsListenerCallback = new VcsListenerCallback();

        public MyVcsListener() {
        }

        public MyVcsListener(String path) {
            this.path = path;
        }

        @Override
        public void onTranscriptionClose() {
            System.out.println("websocket closed");
        }

        @Override
        public void onTranscriptionConnect() {
            System.out.println("websocket connected");
        }

        @Override
        public void onTranscriptionResponse(VcsDataResponse response) {
            System.out.println("receive binary data " + response.getData().length);
            if (fos == null) {
                return;
            }
            try {
                fos.write(response.getData());
                fos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onTranscriptionBegin(StateResponse response) {
            printResponse(response);
            try {
                if (StringUtils.isNullOrEmpty(path)) {
                    return;
                }
                File f = new File(path);
                fos = new FileOutputStream(f);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSTranscriptionEnd(StateResponse response) {
            vcsListenerCallback.onEnd();
            printResponse(response);
            close();
        }

        @Override
        public void onTranscriptionFail(StateResponse response) {
            vcsListenerCallback.onFail();
            printResponse(response);
            close();
        }

        private void close() {
            if (fos == null) {
                return;
            }
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void onCallback(VcsListenerCallback callback) {
            if (callback == null) {
                return;
            }
            this.vcsListenerCallback = callback;
        }

        public static class VcsListenerCallback {
            public void onEnd() {
            }

            public void onFail() {
            }
        }
    }

    private void process() {
        try {
            // 1. 实现监听器接口VcsListener，用户自定义收到响应的处理逻辑
            MyVcsListener vcsListener = new MyVcsListener(audioPath);

            // 2. 初始化RVcsClient
            AuthInfo authInfo = new AuthInfo(ak, sk, region, projectId);
            RvcsClient rvcsClient = new RvcsClient(authInfo, vcsListener, getConfig());
            // 设置回调关闭客户端。此步一定要实施，否则数据处理结束后服务端会因为20s没有接受任何消息而报异常。
            vcsListener.onCallback(new MyVcsListener.VcsListenerCallback() {
                @Override
                public void onEnd() {
                    super.onEnd();
                    rvcsClient.close();
                }

                @Override
                public void onFail() {
                    super.onFail();
                    rvcsClient.close();
                }
            });

            // 3. 配置参数
            VcsRequest vcsRequest = new VcsRequest();
            setParameters(vcsRequest);

            // 4. 建立websocket连接
            rvcsClient.connect(vcsRequest);

            // 5. 发送开始请求、发送文本、发送end请求
            if ("ONLY".equals(vcsRequest.getTextPieces())) {
                // ONLY 模式仅需发送start命令，合成文本须随start命令一起发送
                vcsRequest.setText("欢迎使用华为云语音交互服务。");
                rvcsClient.sendStart();
            } else {
                // 发送开始请求，即将开始请求连带配置发送至服务端
                rvcsClient.sendStart();

                // 发送文本，可连续发送多次，合成结果可以通过监听器获取
                // 方法一,多次发送文本
                rvcsClient.sendMsg("你好");
                rvcsClient.sendMsg("欢迎使用华为云语音交互服务。");
                // 方法二,传入文本路径
                //rvcsClient.sendMsgBatch(textPath); // 传入待合成文本储存路径

                // 发送结束请求
                rvcsClient.sendEnd();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void printResponse(Object response) {
        try {
            System.out.println(JsonUtils.obj2Str(response, true));
        } catch (SisException e) {
            e.printStackTrace();
        }
    }
}
