package com.tongyi.videochatdemo;

import com.tongyi.video_chat_sdk.Constant;
import com.tongyi.video_chat_sdk.conv.ConvConstants.DialogState;

import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.alivc.rtc.AliRtcEngine.AliRtcAudioProfile;
import com.tongyi.video_chat_sdk.Constant.ChatMessageType;
import com.tongyi.video_chat_sdk.Constant.TYErrorKey.VoiceChat;
import com.tongyi.video_chat_sdk.Constant.TYVoiceChatMode;
import com.tongyi.video_chat_sdk.Constant.TYVolumeSourceType;
import com.tongyi.video_chat_sdk.TYVideoChat;
import com.tongyi.video_chat_sdk.data.IChatCallback;
import com.tongyi.video_chat_sdk.data.TYError;
import com.tongyi.video_chat_sdk.data.TYVoiceChatMessage;
import com.tongyi.video_chat_sdk.data.request.TYDialogConfig;
import com.tongyi.video_chat_sdk.data.response.TYAvatarInitData;
import com.tongyi.video_chat_sdk.util.DeviceUtil;
import com.tongyi.video_chat_sdk.util.ThreadPoolUtil;
import com.tongyi.videochatdemo.widget.wavebar.RecognitionProgressView;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;

/**
 * @author flyingfish
 * @date 2024/08/15
 * description:
 */
public class VideoChatActivity extends AppCompatActivity {
    private static final String TAG = VideoChatActivity.class.getSimpleName();
    protected static final String KEY_AUTH_PARAM = "KEY_AVATAR_INIT_DATA", KEY_REQUEST_CONFIG = "KEY_DIALOG_CONFIG";
    private RelativeLayout rootView;
    private EditText tvLogs;
    private TextView tvState;
    private RecognitionProgressView progressView;
    private ViewGroup push2TalkView;
    private CheckBox cbMute;
    private CheckBox cbShowContent;
    private EditText tvPerformanceMonitor;

    private TYVideoChat tyVideoChat;
    private TYAvatarInitData tyAvatarInitData;
    private TYDialogConfig tyDialogConfig;

    /////////////////////////////////////// 启动配置相关Begin ///////////////////////////////////////

    public static boolean launchVideoChat(AppCompatActivity activity, TYAvatarInitData avatarInitData,
                                          TYDialogConfig dialogConfig) {
        Intent launchIntent = new Intent(activity, VideoChatActivity.class);
        Log.d(TAG, "launchVideoChat:avatarInitData:" + avatarInitData + ";dialogConfig:" + dialogConfig);
        launchIntent.putExtra(KEY_AUTH_PARAM, avatarInitData);
        launchIntent.putExtra(KEY_REQUEST_CONFIG, dialogConfig);
        activity.startActivity(launchIntent);
        return true;
    }

    private void handleLaunchParams() {
        tyAvatarInitData = (TYAvatarInitData) getIntent().getSerializableExtra(KEY_AUTH_PARAM);
        tyDialogConfig = (TYDialogConfig) getIntent().getSerializableExtra(KEY_REQUEST_CONFIG);
        Log.d(TAG, "handleLaunchParams:authParams:" + tyAvatarInitData + ";requestConfig:" + tyDialogConfig);
    }

    /////////////////////////////////////// UI相关Begin ///////////////////////////////////////

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DeviceUtil.setStatusBarColor(this);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        handleLaunchParams();
        setContentView(R.layout.activity_video_chat);

        rootView = findViewById(R.id.rl_root);
        tvLogs = findViewById(R.id.tvLogs);
        tvLogs.setMovementMethod(ScrollingMovementMethod.getInstance());
        tvState = findViewById(R.id.tvState);

        tvPerformanceMonitor = findViewById(R.id.tvPerformanceMonitor);

        cbMute = findViewById(R.id.cb_mute);
        cbMute.setOnCheckedChangeListener((buttonView, isChecked) -> tyVideoChat.muteLocalMic(isChecked));
        cbShowContent = findViewById(R.id.cb_show_content);
        cbShowContent.setOnCheckedChangeListener(((buttonView, isChecked) -> tvLogs.setVisibility(isChecked ? View.VISIBLE : View.GONE)));

        progressView = findViewById(R.id.rpv_audio);
        int[] heights = {16, 32, 12, 28, 18};
        progressView.setSingleColor(getColor(R.color.white));
        progressView.setBarMaxHeightsInDp(heights);
        progressView.setCircleRadiusInDp(4);
        progressView.setSpacingInDp(4);
        progressView.play();

        findViewById(R.id.tvState).setOnClickListener(v -> {
            if (sdkIsReady) {
                if (isPush2TalkMode()) {
                    Log.e(TAG, "push 2 talk cannot interrupt");
                } else {
                    tyVideoChat.interrupt();
                }
            }
        });

        findViewById(R.id.tv_back).setOnClickListener(v -> finish());

        push2TalkView = findViewById(R.id.ll_push_2_talk);
        //push2TalkView.setVisibility(isPush2TalkMode() ? View.VISIBLE : View.INVISIBLE);
        push2TalkView.setOnTouchListener((v, event) -> {
            push2TalkView.performClick();
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Log.d(TAG, "onActionDown:eventX:" + event.getX());
                    tyVideoChat.startSpeech();
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    boolean xInViewRegion = event.getX() > 0 && event.getX() < v.getWidth();
                    boolean yInViewRegion = event.getY() > 0 && event.getY() < v.getHeight();
                    boolean isCanceled = !xInViewRegion || !yInViewRegion;
                    Log.d(TAG, "onActionUp:eventX:" + event.getX() + ";eventY:" + event.getY()
                        + ";isCanceled:" + isCanceled);

                    // 松开手指时，离开按键区域，取消发送，在按键区域内，发送音频
                    if (isCanceled) {
                        tyVideoChat.cancelSpeech();
                    } else {
                        tyVideoChat.stopSpeech();
                    }
                    break;
                default:
                    break;
            }
            return false;
        });

        // startSdk
        tyVideoChat = TYVideoChat.getInstance();
        // config audio profile added by flyingfish 2025.04.16
        tyVideoChat.getRtcConfig().setAudioProfile(AliRtcAudioProfile.AliRtcEngineBasicQualityMode);
        // config audio volume value in [0,400]
        tyVideoChat.getRtcConfig().setPlayOutAudioVolume(100);
        tyVideoChat.getRtcConfig().setRecordAudioVolumeBeforeVAD(100);

        tyVideoChat.init(this, tyAvatarInitData, tyDialogConfig);
        tyVideoChat.start(chatCallback);
        tvState.setText("连接中...");

        findViewById(R.id.tv_request_to_respond_prompt).setOnClickListener(v-> {
            if (sdkIsReady) {
                String content = "请说个故事";
                tyVideoChat.requestToRespond("prompt", content);
                Toast.makeText(VideoChatActivity.this, "RequestToRespond:" + content, Toast.LENGTH_SHORT).show();
            }
        });

        findViewById(R.id.tv_request_to_respond_transcript).setOnClickListener(v-> {
            if (sdkIsReady) {
                String content = "请说个故事";
                tyVideoChat.requestToRespond("transcript", content);
                Toast.makeText(VideoChatActivity.this, "RequestToRespond:" + content, Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e("TYVideoChat", "call onStop");
        if (isFinishing()) {
            Log.e("TYVideoChat", "call onStop isFinishing release sdk");
            // exit是同步方法，放到子线程调用
            ThreadPoolUtil.runOnSubThread(() -> {
                tyVideoChat.exit();
                tyVideoChat = null;
            });
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        finish();
    }

    private void appendLogMessage(String message) {
        appendLogMessage(tvLogs, message);
    }

    private void appendLogMessage(EditText tv, String message) {
        runOnUiThread(() -> {
            if ((tv == tvLogs || tv == tvPerformanceMonitor) && cbShowContent.isChecked()) {
                tv.setVisibility(View.VISIBLE);
            }
            tv.setText(message);
            tv.setSelection(tv.getText().length());
        });
    }

    private boolean isPush2TalkMode() {
        return TYVoiceChatMode.PUSH2TALK.equals(tyDialogConfig.getMode());
    }

    /////////////////////////////////////// 启动逻辑相关 ///////////////////////////////////////

    /**
     * sdk start后需要做一些初始化和准备工作，完成后将此标志置为true，此时可以调用业务接口，false时调用业务接口会失败
     */
    private boolean sdkIsReady = false;

    /**
     * 对话过程回调
     */
    private final IChatCallback chatCallback = new IChatCallback() {

        DialogState dialogState = DialogState.DIALOG_IDLE;

        @Override
        public void onStartResult(boolean isSuccess, TYError errorInfo) {
            Log.d(TAG, "onStartResult:isSuccess:" + isSuccess + ";errorInfo:" + errorInfo);
            appendLogMessage(isSuccess ? "鉴权、加入RTC通道成功" : ("鉴权失败:" + errorInfo));
            runOnUiThread(() -> {
                push2TalkView.setVisibility(isPush2TalkMode() ? View.VISIBLE : View.INVISIBLE);

                // Push2Talk不让用户mute麦克风，没有意义
                cbMute.setVisibility(isPush2TalkMode() ? View.GONE : View.VISIBLE);
            });
        }

        @Override
        public void onInterruptResult(boolean isSuccess, @Nullable TYError errorInfo) {
            Log.d(TAG, "onInterruptResult:isSuccess:" + isSuccess + ";errorInfo:" + errorInfo);
        }

        @Override
        public void onReadyToSpeech() {
            Log.d(TAG, "onReadyToSpeech:isPush2Talk:" + isPush2TalkMode());
            sdkIsReady = true;
        }

        @Override
        public void onStateChanged(@NonNull DialogState state) {
            runOnUiThread(() -> {
                dialogState = state;
                String stateMsg = "idle";
                progressView.setVisibility(
                    state == DialogState.DIALOG_LISTENING ? View.VISIBLE : View.INVISIBLE);
                tvLogs.setVisibility(View.GONE);

                // @TODO push2talk 暂时不支持打断，在思考和说的状态时，主动隐藏
                boolean stateOfRespondingOrThinking =
                    state == DialogState.DIALOG_RESPONDING || state == DialogState.DIALOG_THINKING;
                push2TalkView.setVisibility(
                    isPush2TalkMode() && !stateOfRespondingOrThinking ? View.VISIBLE : View.INVISIBLE);

                if (state == DialogState.DIALOG_IDLE) {
                    stateMsg = "Idle";
                } else if (state == DialogState.DIALOG_LISTENING) {
                    stateMsg = "你说，我正在听";
                } else if (state == DialogState.DIALOG_RESPONDING) {
                    stateMsg = isPush2TalkMode() ? "我正在说，暂不支持打断" : "我正在说，轻点可打断我";
                    if (cbShowContent.isChecked()) tvLogs.setVisibility(View.VISIBLE);
                } else {
                    stateMsg = "我正在想";
                }
                tvState.setText(stateMsg);
            });
        }

        @Override
        public void onVolumeChanged(float audioLevel, @NonNull TYVolumeSourceType audioType) {
            runOnUiThread(() -> {
                // 音量条的波动效果，可自行调整
                //Log.d(TAG, "onConvDataSendCallback level:" + audioLevel + ";audioType:" + audioType);
                if (audioType == TYVolumeSourceType.MIC) {
                    progressView.onRmsChanged(audioLevel * 5f);
                }
            });
        }

        @Override
        public void onMessageReceived(@NonNull TYVoiceChatMessage chatMessage) {
            Log.d(TAG, "onMessageReceived:" + chatMessage);
            String messageFrom = chatMessage.getChatMessageType() == ChatMessageType.SPEAKING ? "Me" : "AI";
            appendLogMessage(messageFrom + ":" + chatMessage.getChatMessageText());
        }

        @Override
        public void onGotRenderView(@NonNull SurfaceView renderView) {
            runOnUiThread(() -> {
                findViewById(R.id.tv_back).setVisibility(View.VISIBLE);
                tvLogs.setVisibility(View.GONE);

                if (renderView == rootView.getChildAt(0)) {
                    Log.e(TAG, "render view already added");
                    return;
                }

                LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT);
                renderView.setOnClickListener(v -> {
                    if (sdkIsReady) {
                        if (isPush2TalkMode()) {
                            Log.e(TAG, "push 2 talk cannot interrupt");
                        } else {
                            tyVideoChat.interrupt();
                        }
                    }
                });
                rootView.addView(renderView, 0, layoutParams);
            });
        }

        @Override
        public void onErrorReceived(@NonNull TYError errorInfo) {
            Log.e(TAG, "onErrorReceived:" + errorInfo);
            appendLogMessage("onErrorReceived:" + errorInfo);

            // 如果连接成功后，非绿网错误码，需要用户主动重新调用exit->start接口，重开会话
            if (errorInfo.getKey() != VoiceChat.ERROR_CODE_GREEN_NET_FAILED) {
                return;
            }

            // 触发绿网特殊处理
            // 服务应该采取的交互类型，transcript 表示直接把文本转语音，prompt 表示把文本送大模型回答
            ThreadPoolUtil.runOnSubThread(() -> {
                // 收到绿网消息后，先等300ms，待对讲状态切为Listening后，调用requestToRespond，让服务端播放指定内容
                // 此消息未必一定成功，requestToRespond和实时的语音输入优先级一样，服务端只会处理最先到达的消息
                // 若要保证一定成功，可以先调用muteLocalMic接口，由业务自行决定
                SystemClock.sleep(300);
                Log.e("TYVideoChat", "onErrorReceived onGotGreenNet Code, after 3s, state is：" + dialogState);
                if (dialogState != DialogState.DIALOG_LISTENING) {
                    return;
                }
                String type = "transcript";
                String text = "不好意思，请换个话题试试";
                if (VoiceChat.ERROR_CODE_GREEN_NET_FAILED == errorInfo.getKey()) {
                    tyVideoChat.requestToRespond(type, text);
                }
            });
        }

        @Override
        public void onPerformanceInfoTrack(@NonNull Constant.TYPerformanceInfoType performanceInfoType, @NonNull String performanceInfo) {
            Log.d(TAG, "onPerformanceInfoTrack:" + performanceInfo);
            if (performanceInfoType == Constant.TYPerformanceInfoType.TIMESTAMP_STATISTICS) {
                // 耗时统计
                try {
                    JSONObject debugInfoObject = new JSONObject(performanceInfo);
                    if (!debugInfoObject.has("llmRequestTime")) {
                        return;
                    }
                    long llmFirstTokenTime = debugInfoObject.getLong("llmFirstTokenTime");
                    long llmRequestTime = debugInfoObject.getLong("llmRequestTime");
                    long ttsFirstByteTime = debugInfoObject.getLong("ttsFirstByteTime");
                    long ttsRequestTime = debugInfoObject.getLong("ttsRequestTime");
                    long respondingStartTime = debugInfoObject.getLong("respondingStartTime");

                    StringBuilder builder = new StringBuilder();
                    builder.append("LLM:" + (llmFirstTokenTime - llmRequestTime) + "\n");
                    builder.append("TTS:" + (ttsFirstByteTime - ttsRequestTime) + "\n");
                    builder.append("Avatar:" + (respondingStartTime - ttsFirstByteTime));

                    appendLogMessage(tvPerformanceMonitor, builder.toString());
                } catch (JSONException e) {
                    Log.e(TAG, "Failed to parse JSON", e);
                }
            }
        }

        @Override
        public void onMotionDataReceived(@NonNull String[] strings, @NonNull ArrayList<ArrayList<Float>> arrayList, @NonNull byte[] bytes, long l, boolean b, long l1, @NonNull byte[] bytes1, @NonNull String s) {

        }
    };
}

