package com.vsu.fcounter.speech;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Environment;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.baidu.tts.chainofresponsibility.logger.LoggerProxy;
import com.baidu.tts.client.SpeechError;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.TtsMode;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.vsu.fcounter.speech.control.InitConfig;
import com.vsu.fcounter.speech.control.MySyntherizer;
import com.vsu.fcounter.speech.util.OfflineResource;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by zeven on 2018/1/11.
 */

class SpeechModule extends ReactContextBaseJavaModule {

    private final static String appId = "20520511";
    private final static String appKey = "Gt7qxyIoXITNbhfCXFOT2jPL";
    private final static String secretKey = "Bpi2aFn6WoLEGS6ZRSHy1sw01QUl9YGM";
    private final static String offlineVoice = OfflineResource.VOICE_FEMALE;
    private final static TtsMode ttsMode = TtsMode.MIX;
    private MySyntherizer synthesizer;
    private ReactContext reactContext;
    private String audioPath = Environment.getExternalStorageDirectory() + "/bdAudio";
    private BufferedOutputStream Buff;

    SpeechModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        initPermission();
        initialTts();
    }

    @Override
    public String getName() {
        return "Speech";
    }

    private void emit(String event, Object map) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(event, map);
    }

    /**
     * 初始化引擎，需要的参数均在InitConfig类里
     * <p>
     * DEMO中提供了3个SpeechSynthesizerListener的实现
     * MessageListener 仅仅用log.i记录日志，在logcat中可以看见
     * UiMessageListener 在MessageListener的基础上，对handler发送消息，实现UI的文字更新
     * FileSaveListener 在UiMessageListener的基础上，使用 onSynthesizeDataArrived回调，获取音频流
     */
    private void initialTts() {
        LoggerProxy.printable(false);
        Map<String, String> params = getParams();
        InitConfig initConfig = new InitConfig(appId, appKey, secretKey, ttsMode, params, new SpeechSynthesizerListener() {
            @Override
            public void onSynthesizeStart(String s) {
                emit("onSynthesizeStart", s);
                prepareLocalfile(s);
            }

            @Override
            public void onSynthesizeDataArrived(String s, byte[] bytes, int i, int i1) {
                if (bytes != null) {
                    appendLocalfileSection(bytes);
                }
            }
            @Override
            public void onSynthesizeFinish(String s) {
                emit("onSynthesizeFinish", s);
                endLocalFileData();
            }
            @Override
            public void onSpeechStart(String s) {
                emit("onSpeechStart", s);
            }
            @Override
            public void onSpeechProgressChanged(String s, int i) {
                emit("onSpeechProgressChanged", i);
            }
            @Override
            public void onSpeechFinish(String s) {
                emit("onSpeechFinish", s);
            }
            @Override
            public void onError(String s, SpeechError speechError) {
                emit("onError", s);
            }
        });
        synthesizer = MySyntherizer.getInstance(reactContext, initConfig, null); // 此处可以改为MySyntherizer 了解调用过程
    }

    private void prepareLocalfile(String utteranceId) {
        String fileName = utteranceId + ".pcm";
        File folder = new File(audioPath);
        if (!folder.exists()) {/* 判断文件夹是否存在（不存在则创建这个文件夹） */
            folder.mkdirs();/* 创建文件夹 */
        }

        try {
            File file = new File(audioPath, fileName);
            FileOutputStream outSTr = new FileOutputStream(file);
            Buff = new BufferedOutputStream(outSTr);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void appendLocalfileSection(byte[] buffer) {
        try {
            Buff.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void endLocalFileData() {
        try {
            Buff.flush();
            Buff.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean playAudioTrack(String utteranceId) throws IOException {
        File file = new File(audioPath, utteranceId + ".pcm");
        if (!file.exists()) return false;
        int frequence = 16000;
        int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
        int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
        int streamType = AudioManager.STREAM_MUSIC;
        int bufferSize = AudioTrack.getMinBufferSize(frequence, channelConfig, audioEncoding);
        byte[] buffer = new byte[bufferSize];
        FileInputStream is = new FileInputStream(file);
        DataInputStream dis = new DataInputStream(new BufferedInputStream(is));
        AudioTrack track = new AudioTrack(streamType, frequence, channelConfig, audioEncoding, bufferSize,
                AudioTrack.MODE_STREAM);
        track.setPlaybackRate(frequence);
        /* start play */
        track.setStereoVolume(1.0f, 1.0f);
        track.play();
        while (dis.available() > 0) {
            int i = 0;
            while (dis.available() > 0 && i < buffer.length) {
                buffer[i] = dis.readByte();
                i++;
            }
            /*write data to AudioTrack*/
            track.write(buffer, 0, buffer.length);
        }
        /*stop play*/
        track.stop();
        dis.close();
        return true;
    }


    /**
     * android 6.0 以上需要动态申请权限
     */
    private void initPermission() {
        String[] permissions = {
                Manifest.permission.INTERNET,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.MODIFY_AUDIO_SETTINGS,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_SETTINGS,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.ACCESS_WIFI_STATE,
                Manifest.permission.CHANGE_WIFI_STATE
        };

        ArrayList<String> toApplyList = new ArrayList<>();
        for (String perm : permissions) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(reactContext, perm)) {
                toApplyList.add(perm);
                // 进入到这里代表没有权限.
            }
        }
        String[] tmpList = new String[toApplyList.size()];
        if (!toApplyList.isEmpty() && getCurrentActivity()!=null) {
            ActivityCompat.requestPermissions(getCurrentActivity(), toApplyList.toArray(tmpList), 123);
        }
    }

    /**
     * 合成的参数，可以初始化时填写，也可以在合成前设置。
     */
    private Map<String, String> getParams() {
        Map<String, String> params = new HashMap<>();
        // 以下参数均为选填
        // 设置在线发声音人： 0 普通女声（默认） 1 普通男声 2 特别男声 3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
        params.put(SpeechSynthesizer.PARAM_SPEAKER, "0");
        // 设置合成的音量，0-9 ，默认 5
        params.put(SpeechSynthesizer.PARAM_VOLUME, "9");
        // 设置合成的语速，0-9 ，默认 5
        params.put(SpeechSynthesizer.PARAM_SPEED, "5");
        // 设置合成的语调，0-9 ，默认 5
        params.put(SpeechSynthesizer.PARAM_PITCH, "5");
        params.put(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);
        // 该参数设置为TtsMode.MIX生效。即纯在线模式不生效。
        // MIX_MODE_DEFAULT 默认 ，wifi状态下使用在线，非wifi离线。在线状态下，请求超时6s自动转离线
        // MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI wifi状态下使用在线，非wifi离线。在线状态下， 请求超时1.2s自动转离线
        // MIX_MODE_HIGH_SPEED_NETWORK ， 3G 4G wifi状态下使用在线，其它状态离线。在线状态下，请求超时1.2s自动转离线
        // MIX_MODE_HIGH_SPEED_SYNTHESIZE, 2G 3G 4G wifi状态下使用在线，其它状态离线。在线状态下，请求超时1.2s自动转离线
        // 离线资源文件
        OfflineResource offlineResource = createOfflineResource(offlineVoice);
        // 声学模型文件路径 (离线引擎使用), 请确认下面两个文件存在
        params.put(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, offlineResource.getTextFilename());
        params.put(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE,
                offlineResource.getModelFilename());
        return params;
    }

    private OfflineResource createOfflineResource(String voiceType) {
        OfflineResource offlineResource = null;
        try {
            offlineResource = new OfflineResource(reactContext, voiceType);
        } catch (IOException e) {
            // IO 错误自行处理
            e.printStackTrace();
        }
        return offlineResource;
    }

    /**
     * speak 实际上是调用 synthesize后，获取音频流，然后播放。
     * 获取音频流的方式见SaveFileActivity及FileSaveListener
     * 需要合成的文本text的长度不能超过1024个GBK字节。
     */
    @ReactMethod
    private void speak(String text, Promise promise) {
        new Thread(() -> {
            // 需要合成的文本text的长度不能超过1024个GBK字节。
            // 合成前可以修改参数：
            // Map<String, String> params = getParams();
            // synthesizer.setParams(params);
            boolean suc = false;
            int result = 0;
            try {
                suc =  this.playAudioTrack(text);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (!suc) result = synthesizer.speak(text);
            WritableMap map = Arguments.createMap();
            map.putInt("code", result);
            promise.resolve(map);
        }).start();
    }

    /**
     * 暂停播放。仅调用speak后生效
     */
    @ReactMethod
    private void pause(Promise promise) {
        int result = synthesizer.pause();
        WritableMap map = Arguments.createMap();
        map.putInt("code", result);
        promise.resolve(map);
    }

    /**
     * 继续播放。仅调用speak后生效，调用pause生效
     */
    @ReactMethod
    private void resume(Promise promise) {
        int result = synthesizer.resume();
        WritableMap map = Arguments.createMap();
        map.putInt("code", result);
        promise.resolve(map);
    }

    /**
     * 停止合成引擎。即停止播放，合成，清空内部合成队列。
     */
    @ReactMethod
    private void stop(Promise promise) {
        int result = synthesizer.stop();
        WritableMap map = Arguments.createMap();
        map.putInt("code", result);
        promise.resolve(map);
    }

}
