package com.example.ai_assistant_plugin;

import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.NonNull;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.idst.nui.Constants;
import com.alibaba.idst.nui.INativeStreamInputTtsCallback;
import com.alibaba.idst.nui.NativeNui;

/**
 * AiAssistantPlugin
 */
public class AiAssistantPlugin implements FlutterPlugin, MethodCallHandler {

    private static final String CHANNEL = "com.example.ai_chat_assistant/ali_sdk";
    private static final String TAG = "AliyunSDK";
    private static final String APP_KEY = "bXFFc1V65iYbW6EF";
    private static final String ACCESS_KEY = "LTAI5t71JHxXRvt2mGuEVz9X";
    private static final String ACCESS_KEY_SECRET = "WQOUWvngxmCg4CxIG0qkSlkcH5hrVT";
    private static final String URL = "wss://nls-gateway-cn-beijing.aliyuncs.com/ws/v1";
    private static final String chineseVoice = "zhitian_emo";
    private static final String englishVoice = "abby";
    private final NativeNui streamInputTtsInstance = new NativeNui(Constants.ModeType.MODE_STREAM_INPUT_TTS);

    // asr 是否初始化
    private boolean isAsrInitialized = false;

    // asr property
    private MethodChannel channel;
    // asr instance
    private final NativeNui asrInstance = new NativeNui();
    private Handler asrHandler;
    private AsrCallBack asrCallBack;
    private Handler handler = new Handler(Looper.getMainLooper());

    private final AudioPlayer ttsAudioTrack = new AudioPlayer(new AudioPlayerCallback() {
        @Override
        public void playStart() {
            Log.i(TAG, "start play");
        }

        @Override
        public void playOver() {
            Log.i(TAG, "play over");
        }

        @Override
        public void playSoundLevel(int level) {
        }
    });

    void initAsrInstance() {
        if (!isAsrInitialized) {
            Log.i(TAG, "Initializing ASR for the first time...");
            asrCallBack = new AsrCallBack(channel);
            int initResult = asrInstance.initialize(asrCallBack, genAsrInitParams(),
                    Constants.LogLevel.LOG_LEVEL_NONE, false);
            Log.i(TAG, "ASR initialization result: " + initResult);
            if (initResult != Constants.NuiResultCode.SUCCESS) {
                Log.i(TAG, "ASR initialization failed with error code: " + initResult);
                return;
            }
            isAsrInitialized = true;
        }
    }

    @Override
    public void onAttachedToEngine(FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL);
        channel.setMethodCallHandler(this);
        HandlerThread asrHandlerThread = new HandlerThread("process_thread");
        asrHandlerThread.start();
        asrHandler = new Handler(asrHandlerThread.getLooper());
        // 不能在这里初始化 asr, 初始化之后会占用麦克风
//        asrCallBack = new AsrCallBack(channel);
//        asrInstance.initialize(asrCallBack, genAsrInitParams(),
//                Constants.LogLevel.LOG_LEVEL_NONE, false);
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
        ttsAudioTrack.stop();
        ttsAudioTrack.releaseAudioTrack();
        streamInputTtsInstance.stopStreamInputTts();
        streamInputTtsInstance.release();

        if (isAsrInitialized) {
            asrInstance.release();
            if (asrCallBack != null) {
                asrCallBack.release();
                asrCallBack = null;
            }
            isAsrInitialized = false;
        }

//        asrInstance.release();
//        if (asrCallBack != null) {
//            asrCallBack.release();
//            asrCallBack = null;
//        }
    }

    @Override
    public void onMethodCall(MethodCall call, @NonNull Result result) {
        Map<String, Object> args = call.arguments();
        switch (call.method) {
            case "startTts":
                assert args != null;
                Object isChinese = args.get("isChinese");
                if (isChinese == null || isChinese.toString().isBlank()) {
                    return;
                }
                boolean isSuccess = startTts(Boolean.parseBoolean(isChinese.toString()));
                result.success(isSuccess);
                break;
            case "sendTts":
                assert args != null;
                Object textArg = args.get("text");
                if (textArg == null || textArg.toString().isBlank()) {
                    return;
                }
                sendTts(textArg.toString());
                break;
            case "completeTts":
                completeTts();
                break;
            case "stopTts":
                stopTts();
                break;
            case "startAsr":
                startAsr();
                break;
            case "stopAsr":
                stopAsr();
                break;
            default:
                result.notImplemented();
                break;
        }
    }

    private void sendTts(String text) {
        streamInputTtsInstance.sendStreamInputTts(text);
    }

    private void completeTts() {
        streamInputTtsInstance.stopStreamInputTts();
    }

    private void stopTts() {
        streamInputTtsInstance.cancelStreamInputTts();
        ttsAudioTrack.stop();
    }

    private void startAsr() {
        asrHandler.post(() -> {
            // 初始化
            initAsrInstance();
            // 启动
            String setParamsString = genAsrParams();
            Log.i(TAG, "nui set params " + setParamsString);
            asrInstance.setParams(setParamsString);
            int ret = asrInstance.startDialog(Constants.VadMode.TYPE_P2T, genAsrDialogParams());
            Log.i(TAG, "start done with " + ret);
        });
    }

    private void stopAsr() {
        asrHandler.post(() -> {
            long ret = asrInstance.stopDialog();
            handler.post(() -> channel.invokeMethod("onAsrStop", null));
            Log.i(TAG, "cancel dialog " + ret + " end");
        });
    }

    private String genTtsTicket() {
        String str = "";
        try {
            Auth.GetTicketMethod method = Auth.GetTicketMethod.GET_ACCESS_IN_CLIENT_FOR_ONLINE_FEATURES;
            Auth.setAppKey(APP_KEY);
            Auth.setAccessKey(ACCESS_KEY);
            Auth.setAccessKeySecret(ACCESS_KEY_SECRET);
            Log.i(TAG, "Use method:" + method);
            JSONObject object = Auth.getTicket(method);
            if (!object.containsKey("token") && !object.containsKey("sts_token")) {
                Log.e(TAG, "Cannot get token or sts_token!!!");
            }
            object.put("url", URL);
            str = object.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "user ticket:" + str);
        return str;
    }

    private String genTtsParameters(boolean isChinese) {
        String str = "";
        try {
            JSONObject object = new JSONObject();
            object.put("voice", isChinese ? chineseVoice : englishVoice);
            object.put("format", "pcm");
            object.put("sample_rate", 16000);
            object.put("volume", 100);
            object.put("speech_rate", 50);
            object.put("pitch_rate", 0);
            str = object.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "user parameters:" + str);
        return str;
    }

    private String genAsrInitParams() {
        String str = "";
        try {
            Auth.GetTicketMethod method = Auth.GetTicketMethod.GET_ACCESS_IN_CLIENT_FOR_ONLINE_FEATURES;
            Auth.setAppKey(APP_KEY);
            Auth.setAccessKey(ACCESS_KEY);
            Auth.setAccessKeySecret(ACCESS_KEY_SECRET);
            Log.i(TAG, "Use method:" + method);
            JSONObject object = Auth.getTicket(method);
            if (!object.containsKey("token")) {
                Log.e(TAG, "Cannot get token !!!");
            }
            object.put("device_id", "empty_device_id"); // 必填, 推荐填入具有唯一性的id, 方便定位问题
            object.put("url", URL);
            object.put("service_mode", Constants.ModeFullCloud); // 必填
            str = object.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "InsideUserContext:" + str);
        return str;
    }

    private String genAsrParams() {
        String params = "";
        try {
            JSONObject nls_config = new JSONObject();

            //参数可根据实际业务进行配置
            //接口说明可见https://help.aliyun.com/document_detail/173528.html
            //查看 2.开始识别

            // 是否返回中间识别结果，默认值：False。
            nls_config.put("enable_intermediate_result", true);
            // 是否在后处理中添加标点，默认值：False。
            nls_config.put("enable_punctuation_prediction", true);

            nls_config.put("sample_rate", 16000);
            nls_config.put("sr_format", "pcm");
//            nls_config.put("enable_inverse_text_normalization", true);
//            nls_config.put("max_sentence_silence", 800);
//            nls_config.put("enable_words", false);

            /*若文档中不包含某些参数，但是此功能支持这个参数，可以用如下万能接口设置参数*/
//            JSONObject extend_config = new JSONObject();
//            extend_config.put("custom_test", true);
//            nls_config.put("extend_config", extend_config);

            JSONObject tmp = new JSONObject();
            tmp.put("nls_config", nls_config);
            tmp.put("service_type", Constants.kServiceTypeSpeechTranscriber); // 必填

//            如果有HttpDns则可进行设置
//            tmp.put("direct_ip", Utils.getDirectIp());

            params = tmp.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return params;
    }

    private String genAsrDialogParams() {
        String params = "";
        try {
            JSONObject dialog_param = new JSONObject();
            // 运行过程中可以在startDialog时更新临时参数，尤其是更新过期token
            // 注意: 若下一轮对话不再设置参数，则继续使用初始化时传入的参数
            long distance_expire_time_30m = 1800;
            dialog_param = Auth.refreshTokenIfNeed(dialog_param, distance_expire_time_30m);

            // 注意: 若需要更换appkey和token，可以直接传入参数
//            dialog_param.put("app_key", "");
//            dialog_param.put("token", "");
            params = dialog_param.toString();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "dialog params: " + params);
        return params;
    }

    private boolean startTts(boolean isChinese) {
        int ret = streamInputTtsInstance.startStreamInputTts(new INativeStreamInputTtsCallback() {
            @Override
            public void onStreamInputTtsEventCallback(INativeStreamInputTtsCallback.StreamInputTtsEvent event, String task_id, String session_id, int ret_code, String error_msg, String timestamp, String all_response) {
                Log.i(TAG, "stream input tts event(" + event + ") session id(" + session_id + ") task id(" + task_id + ") retCode(" + ret_code + ") errMsg(" + error_msg + ")");
                if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED) {
                    Log.i(TAG, "STREAM_INPUT_TTS_EVENT_SYNTHESIS_STARTED");
                    ttsAudioTrack.play();
                    Log.i(TAG, "start play");
                } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS) {
                    Log.i(TAG, "STREAM_INPUT_TTS_EVENT_SENTENCE_SYNTHESIS:" + timestamp);
                } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE || event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_TASK_FAILED) {
                    /*
                     * 提示: STREAM_INPUT_TTS_EVENT_SYNTHESIS_COMPLETE事件表示TTS已经合成完并通过回调传回了所有音频数据, 而不是表示播放器已经播放完了所有音频数据。
                     */
                    Log.i(TAG, "play end");

                    // 表示推送完数据, 当播放器播放结束则会有playOver回调
                    ttsAudioTrack.isFinishSend(true);

                    if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_TASK_FAILED) {
                        Log.e(TAG, "STREAM_INPUT_TTS_EVENT_TASK_FAILED: " + "error_code(" + ret_code + ") error_message(" + error_msg + ")");
                    }
                } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN) {
                    Log.i(TAG, "STREAM_INPUT_TTS_EVENT_SENTENCE_BEGIN:" + all_response);
                } else if (event == StreamInputTtsEvent.STREAM_INPUT_TTS_EVENT_SENTENCE_END) {
                    Log.i(TAG, "STREAM_INPUT_TTS_EVENT_SENTENCE_END:" + all_response);
                }
            }

            @Override
            public void onStreamInputTtsDataCallback(byte[] data) {
                if (data.length > 0) {
                    ttsAudioTrack.setAudioData(data);
                }
            }
        }, genTtsTicket(), genTtsParameters(isChinese), "", Constants.LogLevel.toInt(Constants.LogLevel.LOG_LEVEL_NONE), false);
        if (Constants.NuiResultCode.SUCCESS != ret) {
            Log.i(TAG, "start tts failed " + ret);
            return false;
        } else {
            return true;
        }

    }

}
