package com.ch.jedge.jbot;

import com.ch.jedge.utils.JEVdbConfigUtil;
import com.changhong.jedge.JMgbusService;
import com.changhong.jedge.MgbusApi;
import com.changhong.qlib.QData;
import com.changhong.qlib.intf.QIData;
import com.changhong.qlib.util.StringUtils;
import com.changhong.qlib.util.sync.SystemUtils;

import java.util.LinkedList;

public class JedgeVoiceBotService  extends JMgbusService  {
    protected String inputVoiceMode;
    protected String talkUri = "/gpt/knTalk";;
    private static final String vkey_voice_event_text_ = "textOk";
    private static final String vkey_voice_event_wakeup_ = "wakeUp";
    private static final String vkey_voice_text_handle_text_ = "text";
    private static final String vkey_voice_text_handle_end_ = "end";
    private static final String vkey_voice_text_handle_result_type_ = "result";
    private static final String vkey_voice_text_handle_result_response_ = "text";
    private static final String vkey_voice_text_handle_result_sessoin_ = "edge_session";
    private static final String sval_uri_text_handle_result_ = "/dds/textHandleResult";


    boolean vui_going_ = false;
    private boolean quit_ = false;
    private boolean active_ = false;
    private final LinkedList<String> gptSpeakList = new LinkedList<>();

    int timeOutCount = 0;
    int speakCount = 0;
    private final SentenceSplitter sentSpliter = new SentenceSplitter();

    public JedgeVoiceBotService(JedgeLlmBotClient glLlmBot) {
        super(glLlmBot);
        auto_remove_call_info = false;

        if(StringUtils.isNotValidStr(inputVoiceMode))
            inputVoiceMode = JEVdbConfigUtil.getConfigObjSimpleFmtString("jbot","vw", "vui");
        if(StringUtils.isNotValidStr(inputVoiceMode))
            inputVoiceMode="vui";

        module.postThread(this::post2TextSpeech);
        module.postThread(this::checkChatTimeout);
    }

    private void checkChatTimeout() {
        while(!quit_) {
            if(vui_going_) {
                timeOutCount++;
                if(timeOutCount > 30) {
                    module.warnLog("Warning!!! chat return timeout.");
                    vui_going_ = false;
                }
            }
            SystemUtils.tryWait(1000);
        }
    }

    private void post2TextSpeech() {
        while(!quit_) {
            speakCount-=250;
//            QDWarn("speakCount:%d", speakCount);
            if (speakCount < 1) {
                //从队列中提取一个消息发送阅读
                String tts;
                {
                    boolean isEmpty;
                    synchronized (gptSpeakList){
                        isEmpty = gptSpeakList.isEmpty();
                    }
                    if (isEmpty) {
                        SystemUtils.tryWait(250);
                        speakCount = 0;
                        continue;
                    }
                    synchronized (gptSpeakList) {
                        tts = gptSpeakList.pop();
                    }
                }

                if(StringUtils.isNotValidStr(tts))
                    continue;
                if("[DONE]".equals(tts)) {
//                    speakText("完毕");
                    vui_going_ = false;
                    continue;
                } else if (StringUtils.isValidStr(tts)) {
                    //计算speakCount长度
                    speakCount = tts.length() * 71 * 3;
                    module.warnLog(String.format("Reset speakCount:%d", speakCount));
                    speakText(tts);
                    timeOutCount = 0;
                }
            }
            SystemUtils.tryWait(250);
        }
        active_ = false;
    }

    private void speakText(String text) {
        module.exMarkLog(String.format("TO SPEECH=>>:%s", text));
        QIData d = new QData();
        d.putString("text", text).putInteger("p",1);
        postServiceMessage(inputVoiceMode, "/dds/speakText", d);
    }

    @Override
    public void onServiceStart() {
        super.onServiceStart();

        watchLocalEvent("OnModuleConnected", (w, keyVal, msg) -> {
            String host = msg.getString("host");
            int port = msg.getInteger("port");
            this.onMgbusConnected(host, port);
            return true;
        });

        watchLocalEvent("OnModuleDisconnected", (w, keyVal, msg) -> {
            String host = msg.getString("host");
            int port = msg.getInteger("port");
            this.onMgbusDisconnected(host, port);
            return true;
        });
    }

    //module掉线，啥也不做
    private void onMgbusConnected(String host, int port) {
        watchMgbusEvent("online", "vui", msg -> {
            String mode = msg.getString("module");
            String mgnet = msg.getString("mgnet");
            boolean isLocal = msg.getBoolean("local", false);
            if(isLocal && mode.contains(mgnet))
                mode = "vui";
            if (inputVoiceMode.equals(mode) || isLocal) {
                watchVoiceModule();
            }
        });
        watchMgbusEvent("offline", "vui", msg -> {
            String mode = msg.getString("module");
            if (inputVoiceMode.equals(mode)) {
                removeVoiceWatch();
            }
        });
    }

    private void onMgbusDisconnected(String host, int port) {

    }

    private void removeVoiceWatch() {
        clearEventWatcher(inputVoiceMode, "textOk","/@c/_ecb" ,"@");
    }

    private void watchVoiceModule() {
        watchServiceEvent(inputVoiceMode, vkey_voice_event_text_, eMsg -> {
            boolean isFinal = eMsg.getBoolean(vkey_voice_text_handle_end_, false);
            int session = eMsg.getInteger(vkey_voice_text_handle_result_sessoin_);  //可能为空，则单独创建一个对话ID
            String text = eMsg.getString(vkey_voice_text_handle_text_);
            module.exMarkLog(String.format("...>>[text]: %s", text));
            if (isFinal) {
                //auto mod = GetEventSrc(eMsg); //GetEventSrc(eMsg);
                /* !mod.empty() && mod == (inputVoiceMode) &&  */
                if (StringUtils.isValidStr(text) ) {
                    handleGptVoiceText(session, text);
                }
            }
        });

        watchServiceEvent(inputVoiceMode, vkey_voice_event_wakeup_, eMsg -> {
            String deviceId = eMsg.getString("deviceId");
            if(module.getHostSn().contains(deviceId)) {
                //本地唤醒
//                module.exMarkLog(String.format("...>>[wakeupEvent]: %s", deviceId));
                vui_going_ = false;
                synchronized (this.gptSpeakList) {
                    gptSpeakList.clear();
                }
            }
        });

    }

    private void handleGptVoiceText(int session, String text) {
        module.exMarkLog(String.format("[%d]:Voice Text pushed incoming = %s", session, text));    //中间数据
        //设置返回参数
        //处理流程 找到对话，再在对话的处理流程中、处理语义的应答信息。根据不同的语境，做出不同的应答。
        {
            if ((text.startsWith("退出") || text.startsWith("停止"))) {
                synchronized (gptSpeakList) {
                    gptSpeakList.clear();
                }
                //快速回答,接管语义处理
                returnVoiceResult(inputVoiceMode, session, 0, "好的、再见");

                vui_going_ = false;
                timeOutCount = 0;
                return;
            } else if(vui_going_) {
                module.exMarkLog("已在对话中……");
                return;
            } else {
                timeOutCount = 0;
                vui_going_ = true;
                //快速回答,接管语义处理
                returnVoiceResult(inputVoiceMode, session, 0, "稍等");
            }
        }
        QIData r = new QData();
        //发送talk请求到mgs，并将分别返回的结果进入队列
        r.putString("text", text);
        r.putString("knn", "testGPT");
        r.putBoolean("stream", true);
        r.putBoolean("k", true);
        r.putBoolean("v", true);
        r.putString("cb", "/gptv/streamBack");
        r.putBoolean("talkStream", true);     //按句回推
        postServiceMessage("jbot", talkUri, r);
    }

    private void returnVoiceResult(String mod, int session, int type, String answerText) {
        QIData eMsg = new QData();
        eMsg.putInteger(vkey_voice_text_handle_result_type_, type);  //继续对话
        eMsg.putString(vkey_voice_text_handle_result_response_, answerText);
        eMsg.putInteger(vkey_voice_text_handle_result_sessoin_, session);
        postServiceMessage(mod, sval_uri_text_handle_result_, eMsg);
        module.exMarkLog(String.format("Post2Speech:%s", answerText));
    }

    @Override
    public void onServiceStop() {
        this.quit_ = true;
        while(active_) {
            SystemUtils.tryWait(20);
        }
        super.onServiceStop();
    }


    //组装成正常的语句
    @MgbusApi
    public QIData streamBack(QIData req) {
        timeOutCount = 0;
        boolean next_vui_going_ = !req.getBoolean("end", false);
        String streamText = req.getString("msg");
        module.printClientLog(String.format("GPT>>(Going:%s):%s", next_vui_going_?"true":"false", streamText));
        if(StringUtils.isValidStr(streamText) && !streamText.startsWith("[") && !streamText.startsWith("$")) {
            streamText = streamText.replaceAll("\n", ""); //去掉空行
            if (vui_going_ && !streamText.isEmpty()) {
                synchronized (gptSpeakList) {
                    String str = sentSpliter.pushChars(streamText);
                    while (StringUtils.isValidStr(str)) {
                        module.highLog(String.format("GPT>>(Going:%s):%s", vui_going_ ? "true" : "false", str));
                        gptSpeakList.add(str);
                        str = sentSpliter.getNextString();
                    }
                }
            }
        }
        if(!next_vui_going_) {
            String str = sentSpliter.getNextString();
            synchronized (gptSpeakList) {
                if(StringUtils.isValidStr(str)) {
                    gptSpeakList.add(str);
                }
                gptSpeakList.add("[DONE]");
            }
        }
        return null;
    }

    private static class SentenceSplitter {
        StringBuilder buffer = new StringBuilder();
        String leftStr = null;
        public String pushChars(String streamText) {
            int streamTextLen = streamText.length();
            for(int i=0;i<streamTextLen;i++) {
                char subChar = streamText.charAt(i);
                switch (subChar) {
                    case ',':
//                    case '.':
                    case '!':
                    case '；':
                    case '?':
                    case '，':
                    case '。':
                    case '！':
                    case '？':
                        leftStr = streamText.substring(i+1);
                        String result = buffer.toString();
                        buffer = new StringBuilder();
                        return result;
                    default:
                        buffer.append(subChar);
                }
            }
            return null;
        }

        public String getNextString() {
            if(StringUtils.isValidStr(leftStr))
                return pushChars(leftStr);
            else
                return null;
        }
    }
}
