package com.ttstd.controller;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.RadioGroup;
import android.widget.Switch;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.view.Gravity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;

import androidx.appcompat.app.AppCompatActivity;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.ttstd.control.Action;
import com.ttstd.control.ControlMessage;
import com.ttstd.controller.signaling.SignalMessage;
import com.ttstd.controller.signaling.WebSocketClient;
import com.ttstd.controller.view.RemoteTouchView;
import com.ttstd.controller.utils.DeviceUtils;
import com.ttstd.controller.webrtc.SelfCodecDecoder;
import com.ttstd.controller.webrtc.VideoRecorder;
import com.ttstd.controller.webrtc.WebRtcClient;

import org.webrtc.EglBase;
import org.webrtc.IceCandidate;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "ControllerMain";

    private EditText etServerUrl;
    private EditText etDeviceId;
    private EditText etTargetDeviceId;
    private Button btnConnect;
    private Button btnDisconnect;
    private TextView tvStatus;
    private SurfaceViewRenderer remoteVideoView;
    private RemoteTouchView touchOverlay;
    private FrameLayout controlPanel;
    private LinearLayout setupPanel;
    private TextView tvStatusControl;
    private TextView tvStats;
    private Spinner spinnerResolution;
    private Spinner spinnerFps;
    /** 帧率下拉框当前档位（以被控端上报的 supported_fps 为准）。 */
    private final List<Integer> fpsOptions = new ArrayList<>();
    /** 被控端最近上报的实际采集分辨率/帧率（仅切帧率时保持分辨率不变）。 */
    private int lastReportedWidth = 0;
    private int lastReportedHeight = 0;
    private int lastKnownFps = 0;
    private Button btnDisconnectControl;

    // 屏幕串流"自编码"模式相关 UI 与解码器
    private Switch switchStreamMode;
    private TextView tvStreamMode;
    private SurfaceView selfCodecSurface;
    private SelfCodecDecoder selfCodecDecoder;
    private SurfaceHolder.Callback surfaceCallback;
    private boolean programmaticSwitch = false;

    // 屏幕录制相关
    private static final int REQ_RECORD_PERMISSION = 1001;
    private Switch switchRecord;
    private TextView tvRecordStatus;
    private VideoRecorder videoRecorder;
    private boolean isSelfCodecMode = false; // 当前是否为自编码串流模式
    private boolean pendingRecordStart = false;

    private WebSocketClient wsClient;
    private WebRtcClient webRtcClient;
    private EglBase eglBase;
    private final Gson gson = new Gson();
    private String myDeviceId;
    private String targetDeviceId;
    private String pendingAuthType;
    private String pendingAuthValue;

    private final Handler statsHandler = new Handler(Looper.getMainLooper());
    private final Runnable statsRunnable = new Runnable() {
        @Override
        public void run() {
            updateStats();
            statsHandler.postDelayed(this, 1000);
        }
    };

    // 网速统计基线（累计字节数与上次采样时间戳）
    private long prevBytesReceived = 0;
    private long prevBytesSent = 0;
    private long prevStatsTime = 0;

    // 连接建立时的时间戳（毫秒），用于计算连接时长。
    private long connectionStartTime = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        etServerUrl = findViewById(R.id.et_server_url);
        etDeviceId = findViewById(R.id.et_device_id);
        etTargetDeviceId = findViewById(R.id.et_target_device_id);
        btnConnect = findViewById(R.id.btn_connect);
        btnDisconnect = findViewById(R.id.btn_disconnect);
        tvStatus = findViewById(R.id.tv_status);
        remoteVideoView = findViewById(R.id.remote_video_view);
        touchOverlay = findViewById(R.id.touch_overlay);
        controlPanel = findViewById(R.id.control_panel);
        setupPanel = findViewById(R.id.setup_panel);
        tvStatusControl = findViewById(R.id.tv_status_control);
        tvStats = findViewById(R.id.tv_stats);
        spinnerResolution = findViewById(R.id.spinner_resolution);
        spinnerFps = findViewById(R.id.spinner_fps);
        btnDisconnectControl = findViewById(R.id.btn_disconnect_control);

        // 默认服务器地址
        etServerUrl.setText("wss://www.ttstd.com/signal");
        // 生成设备ID（非系统签名，使用兜底方案获取设备标识）
        myDeviceId = DeviceUtils.getSerialNumber(this);
        etDeviceId.setText(myDeviceId);

        btnConnect.setOnClickListener(v -> showAuthDialog());
        btnDisconnect.setOnClickListener(v -> disconnect());
        btnDisconnectControl.setOnClickListener(v -> disconnect());

        // 初始化 EGL 和远端视频渲染
        eglBase = EglBase.create();
        remoteVideoView.init(eglBase.getEglBaseContext(), new RendererCommon.RendererEvents() {
            @Override
            public void onFirstFrameRendered() {
                Log.d(TAG, "First frame rendered");
            }

            @Override
            public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation) {
                Log.d(TAG, "Video resolution changed: " + videoWidth + "x" + videoHeight + " rotation: " + rotation);
                adjustVideoSize(videoWidth, videoHeight, rotation);
            }
        });
        remoteVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        remoteVideoView.setEnableHardwareScaler(true);
        remoteVideoView.setZOrderMediaOverlay(true);

        // 设置触摸事件
        touchOverlay.setTouchEventListener(new RemoteTouchView.TouchEventListener() {
            @Override
            public void onTouch(float relX, float relY) {
                // 已通过 onMotionEvent 处理
            }

            @Override
            public void onSwipe(float relX1, float relY1, float relX2, float relY2, long duration) {
                // 已通过 onMotionEvent 处理
                sendSwipeCommand(relX1, relY1, relX2, relY2, duration);
            }

            @Override
            public void onKeyEvent(int keyCode, int action) {
                sendKeyCommand(keyCode, action);
            }

            @Override
            public void onLongPress(float relX, float relY) {
                // 如果需要高层级的长按，可以保留，但 raw 模式下远程系统会自动识别
                // sendLongPressCommand(relX, relY);
            }

            @Override
            public void onMotionEvent(int action, float relX, float relY) {
                sendMotionEventCommand(action, relX, relY);
            }
        });

        setupResolutionSpinner();
        setupFpsSpinner();
        updateUI(false);

        setupSelfCodecUi();
        setupRecordUi();
    }

    private void adjustVideoSize(int videoWidth, int videoHeight, int rotation) {
        runOnUiThread(() -> {
            if (videoWidth == 0 || videoHeight == 0) return;

            int containerWidth = controlPanel.getWidth();
            int containerHeight = controlPanel.getHeight();

            if (containerWidth == 0 || containerHeight == 0) {
                // 如果容器还没测量好，稍后再试
                controlPanel.postDelayed(() -> adjustVideoSize(videoWidth, videoHeight, rotation), 100);
                return;
            }

            float videoAspectRatio;
            if (rotation == 90 || rotation == 270) {
                videoAspectRatio = (float) videoHeight / videoWidth;
            } else {
                videoAspectRatio = (float) videoWidth / videoHeight;
            }

            float containerAspectRatio = (float) containerWidth / containerHeight;

            int newWidth, newHeight;
            if (videoAspectRatio > containerAspectRatio) {
                // 视频更宽，以容器宽度为准
                newWidth = containerWidth;
                newHeight = (int) (containerWidth / videoAspectRatio);
            } else {
                // 视频更高，以容器高度为准
                newWidth = (int) (containerHeight * videoAspectRatio);
                newHeight = containerHeight;
            }

            // 更新视频渲染视图大小
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) remoteVideoView.getLayoutParams();
            lp.width = newWidth;
            lp.height = newHeight;
            remoteVideoView.setLayoutParams(lp);

            // 更新触摸层大小，使其与视频区域完全重合
            FrameLayout.LayoutParams overlayLp = (FrameLayout.LayoutParams) touchOverlay.getLayoutParams();
            overlayLp.width = newWidth;
            overlayLp.height = newHeight;
            touchOverlay.setLayoutParams(overlayLp);

            Log.d(TAG, String.format("Adjusted video view size: %dx%d (video: %dx%d, rotation: %d, container: %dx%d)",
                    newWidth, newHeight, videoWidth, videoHeight, rotation, containerWidth, containerHeight));
        });
    }

    /**
     * 自编码模式：按比例调整 selfCodecSurface 与触摸层尺寸。
     * MediaCodec 渲染到 SurfaceView 时会把帧拉伸填满整个 Surface 显示区域，
     * 因此必须让 Surface 的宽高比与视频一致，否则画面会被拉伸变形。
     */
    private void adjustSelfCodecSize(int videoWidth, int videoHeight) {
        runOnUiThread(() -> {
            if (videoWidth == 0 || videoHeight == 0) return;

            int containerWidth = controlPanel.getWidth();
            int containerHeight = controlPanel.getHeight();

            if (containerWidth == 0 || containerHeight == 0) {
                // 容器尚未测量完成，稍后重试
                controlPanel.postDelayed(() -> adjustSelfCodecSize(videoWidth, videoHeight), 100);
                return;
            }

            float videoAspectRatio = (float) videoWidth / videoHeight;
            float containerAspectRatio = (float) containerWidth / containerHeight;

            int newWidth, newHeight;
            if (videoAspectRatio > containerAspectRatio) {
                // 视频更宽，以容器宽度为准
                newWidth = containerWidth;
                newHeight = (int) (containerWidth / videoAspectRatio);
            } else {
                // 视频更高，以容器高度为准
                newWidth = (int) (containerHeight * videoAspectRatio);
                newHeight = containerHeight;
            }

            // 调整自编码渲染 Surface 的尺寸（等比居中），避免画面被拉伸变形
            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) selfCodecSurface.getLayoutParams();
            lp.width = newWidth;
            lp.height = newHeight;
            lp.gravity = Gravity.CENTER;
            selfCodecSurface.setLayoutParams(lp);

            // 同步触摸层，使其与视频区域完全重合，保证触控坐标一致
            FrameLayout.LayoutParams overlayLp = (FrameLayout.LayoutParams) touchOverlay.getLayoutParams();
            overlayLp.width = newWidth;
            overlayLp.height = newHeight;
            overlayLp.gravity = Gravity.CENTER;
            touchOverlay.setLayoutParams(overlayLp);

            Log.d(TAG, String.format("Adjusted self-codec view size: %dx%d (video: %dx%d, container: %dx%d)",
                    newWidth, newHeight, videoWidth, videoHeight, containerWidth, containerHeight));
        });
    }

    /** 弹出鉴权输入对话框：选择免密连接、动态验证码或固定密码，输入后发起连接。 */
    private void showAuthDialog() {
        String serverUrl = etServerUrl.getText().toString().trim();
        String target = etTargetDeviceId.getText().toString().trim();
        String myId = etDeviceId.getText().toString().trim();
        if (serverUrl.isEmpty() || target.isEmpty() || myId.isEmpty()) {
            Toast.makeText(this, "请先填写服务器地址、设备ID和目标设备ID", Toast.LENGTH_SHORT).show();
            return;
        }

        View view = getLayoutInflater().inflate(R.layout.dialog_auth_input, null);
        RadioGroup rg = view.findViewById(R.id.rg_auth_type);
        EditText et = view.findViewById(R.id.et_auth_value);
        TextView tip = view.findViewById(R.id.tv_auth_tip);

        // 根据选中的鉴权方式切换输入框与提示文案。默认选中“免密连接”，输入框隐藏。
        rg.setOnCheckedChangeListener((group, checkedId) -> {
            if (checkedId == R.id.rb_none) {
                et.setVisibility(View.GONE);
                tip.setVisibility(View.VISIBLE);
                tip.setText(R.string.auth_none_tip);
            } else if (checkedId == R.id.rb_password) {
                et.setVisibility(View.VISIBLE);
                et.setHint(R.string.auth_password_hint);
                tip.setVisibility(View.VISIBLE);
                tip.setText(R.string.auth_password_tip);
            } else {
                et.setVisibility(View.VISIBLE);
                et.setHint(R.string.auth_code_hint);
                tip.setVisibility(View.VISIBLE);
                tip.setText(R.string.auth_code_tip);
            }
        });

        AlertDialog dialog = new AlertDialog.Builder(this)
                .setTitle("连接鉴权")
                .setView(view)
                .setNegativeButton("取消", null)
                .setPositiveButton("连接", null)
                .create();

        // 校验失败时不关闭对话框，引导用户重新输入
        dialog.setOnShowListener(d -> dialog.getButton(AlertDialog.BUTTON_POSITIVE)
                .setOnClickListener(v -> {
                    int checkedId = rg.getCheckedRadioButtonId();
                    // 免密连接（NONE）无需输入验证码/密码
                    if (checkedId == R.id.rb_none) {
                        pendingAuthType = "NONE";
                        pendingAuthValue = "";
                    } else {
                        boolean isPassword = (checkedId == R.id.rb_password);
                        String authType = isPassword ? "PASSWORD" : "CODE";
                        String authValue = et.getText().toString().trim();
                        if (authValue.isEmpty()) {
                            Toast.makeText(MainActivity.this, "请输入验证码或密码", Toast.LENGTH_SHORT).show();
                            return;
                        }
                        pendingAuthType = authType;
                        pendingAuthValue = authValue;
                    }
                    dialog.dismiss();
                    connectToControlled();
                }));
        dialog.show();
    }

    private void connectToControlled() {
        String serverUrl = etServerUrl.getText().toString().trim();
        targetDeviceId = etTargetDeviceId.getText().toString().trim();
        myDeviceId = etDeviceId.getText().toString().trim();

        if (serverUrl.isEmpty() || targetDeviceId.isEmpty() || myDeviceId.isEmpty()) {
            Toast.makeText(this, "请填写所有字段", Toast.LENGTH_SHORT).show();
            return;
        }

        tvStatus.setText("状态: 正在连接信令服务器...");

        // 初始化 WebSocket
        wsClient = new WebSocketClient(serverUrl, myDeviceId);
        wsClient.setListener(new WebSocketClient.SignalListener() {
            @Override
            public void onConnected() {
                tvStatus.setText("状态: 已连接信令服务器，正在发起连接...");
                // 连接成功后初始化 WebRTC 并创建 Offer
                initWebRtcAndConnect();
            }

            @Override
            public void onDisconnected() {
                tvStatus.setText("状态: 已断开连接");
                updateUI(false);
            }

            @Override
            public void onError(String error) {
                Log.e(TAG, "onError: " + error);
                tvStatus.setText("状态: 连接错误 - " + error);
                updateUI(false);
            }

            @Override
            public void onMessage(SignalMessage message) {
                handleSignalMessage(message);
            }
        });
        wsClient.connect();
    }

    private void initWebRtcAndConnect() {
        webRtcClient = new WebRtcClient(this, wsClient, myDeviceId);
        webRtcClient.initialize(eglBase);
        webRtcClient.setSelfCodecDecoder(selfCodecDecoder);
        webRtcClient.setRecorder(videoRecorder);
        webRtcClient.setStreamModeReportListener(mode -> runOnUiThread(() -> syncStreamModeUi(mode)));
        // 被控端上报当前分辨率/帧率与支持的帧率档位 -> 同步帧率下拉框
        webRtcClient.setResolutionReportListener((w, h, fps, supportedFps) -> runOnUiThread(() -> {
            if (w > 0) lastReportedWidth = w;
            if (h > 0) lastReportedHeight = h;
            if (fps > 0) lastKnownFps = fps;
            applyFpsOptions(supportedFps, fps);
        }));
        webRtcClient.setConnectionListener(new WebRtcClient.ConnectionListener() {
            @Override
            public void onConnectionEstablished() {
                connectionStartTime = System.currentTimeMillis();
                runOnUiThread(() -> {
                    tvStatus.setText("状态: 已连接 - 远程控制中");
                    updateUI(true);
                    touchOverlay.requestFocus();
                });
            }

            @Override
            public void onConnectionFailed(String error) {
                runOnUiThread(() -> {
                    tvStatus.setText("状态: 连接失败 - " + error);
                    updateUI(false);
                });
            }

            @Override
            public void onDisconnected() {
                runOnUiThread(() -> {
                    Toast.makeText(MainActivity.this, "网络连接已断开", Toast.LENGTH_SHORT).show();
                    disconnect();
                    tvStatus.setText("状态: 远端已断开");
                });
            }
        });
        webRtcClient.createOffer(targetDeviceId, remoteVideoView, pendingAuthType, pendingAuthValue);
    }

    private void handleSignalMessage(SignalMessage message) {
        String type = message.getType();
        if (type == null) return;

        switch (type.toUpperCase()) {
            case "ANSWER":
                handleAnswer(message);
                break;
            case "ICE_CANDIDATE":
                handleIceCandidate(message);
                break;
            case "TARGET_OFFLINE":
                handleTargetOffline(message);
                break;
            case "CONNECTION_REJECTED":
                handleConnectionRejected(message);
                break;
            case "REQUEST_ERROR":
            case "REQUEST_TIMEOUT":
                handleRequestAborted(message);
                break;
        }
    }

    private void handleTargetOffline(SignalMessage message) {
        String offlineId = message.getToDeviceId();
        String payload = message.getPayload();
        String text = (payload != null && !payload.isEmpty())
                ? payload
                : "目标被控端不在线，请确认设备已开启并连接服务器";
        String finalText = text;
        runOnUiThread(() -> {
            tvStatus.setText("状态: " + finalText);
            Toast.makeText(MainActivity.this, finalText, Toast.LENGTH_LONG).show();
            updateUI(false);
        });
        Log.w(TAG, "Target device " + offlineId + " is offline");
    }

    /**
     * 被控端拒绝了连接请求：提示用户并复位到未连接状态。
     */
    private void handleConnectionRejected(SignalMessage message) {
        String targetId = message.getToDeviceId();
        String reason = parseReason(message.getPayload());
        String finalText = reason;
        runOnUiThread(() -> {
            tvStatus.setText("状态: " + finalText);
            Toast.makeText(MainActivity.this, finalText, Toast.LENGTH_LONG).show();
            updateUI(false);
        });
        Log.w(TAG, "Connection request to " + targetId + " was rejected: " + reason);
    }

    /** 解析拒绝原因：优先读取 JSON 中的 reason 字段，否则直接返回原文。 */
    private String parseReason(String payload) {
        if (payload == null || payload.isEmpty()) {
            return "目标被控端拒绝了连接请求";
        }
        try {
            JsonObject obj = gson.fromJson(payload, JsonObject.class);
            if (obj != null && obj.has("reason") && !obj.get("reason").isJsonNull()) {
                return obj.get("reason").getAsString();
            }
        } catch (Exception ignored) {
        }
        return payload;
    }

    /**
     * 连接请求被服务器拒绝（REQUEST_ERROR）或应答超时（REQUEST_TIMEOUT）：提示并复位。
     */
    private void handleRequestAborted(SignalMessage message) {
        String payload = message.getPayload();
        String text = (payload != null && !payload.isEmpty())
                ? payload
                : "连接请求失败";
        String finalText = text;
        runOnUiThread(() -> {
            tvStatus.setText("状态: " + finalText);
            Toast.makeText(MainActivity.this, finalText, Toast.LENGTH_LONG).show();
            updateUI(false);
        });
        Log.w(TAG, "Connection request aborted: " + finalText);
    }

    private void handleAnswer(SignalMessage message) {
        try {
            JsonObject payload = gson.fromJson(message.getPayload(), JsonObject.class);
            String sdp = payload.get("sdp").getAsString();
            if (webRtcClient != null) {
                // 被控端已接受连接请求，进入 WebRTC 协商阶段。
                runOnUiThread(() -> tvStatus.setText("状态: 被控端已接受连接，正在建立连接..."));
                webRtcClient.handleAnswer(sdp);
            }
        } catch (Exception e) {
            Log.e(TAG, "Error parsing answer", e);
        }
    }

    private void handleIceCandidate(SignalMessage message) {
        try {
            JsonObject payload = gson.fromJson(message.getPayload(), JsonObject.class);
            String sdpMid = payload.get("sdpMid").getAsString();
            int sdpMLineIndex = payload.get("sdpMLineIndex").getAsInt();
            String candidate = payload.get("candidate").getAsString();

            IceCandidate iceCandidate = new IceCandidate(sdpMid, sdpMLineIndex, candidate);
            if (webRtcClient != null) {
                webRtcClient.addIceCandidate(iceCandidate);
            }
        } catch (Exception e) {
            Log.e(TAG, "Error parsing ICE candidate", e);
        }
    }

    private void sendTouchCommand(float relX, float relY) {
        if (webRtcClient != null) {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.TOUCH)
                    .setX(relX)
                    .setY(relY)
                    .build();
            webRtcClient.sendControlCommand(msg);
        }
    }

    private void sendSwipeCommand(float relX1, float relY1, float relX2, float relY2, long duration) {
        if (webRtcClient != null) {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.SWIPE)
                    .setX1(relX1)
                    .setY1(relY1)
                    .setX2(relX2)
                    .setY2(relY2)
                    .setDuration(duration)
                    .build();
            webRtcClient.sendControlCommand(msg);
        }
    }

    private void sendKeyCommand(int keyCode, int action) {
        if (webRtcClient != null) {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.KEY)
                    .setKeyCode(keyCode)
                    .setKeyAction(action)
                    .build();
            webRtcClient.sendControlCommand(msg);
        }
    }

    private void sendLongPressCommand(float relX, float relY) {
        if (webRtcClient != null) {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.LONG_PRESS)
                    .setX(relX)
                    .setY(relY)
                    .build();
            webRtcClient.sendControlCommand(msg);
        }
    }

    private void sendMotionEventCommand(int action, float relX, float relY) {
        if (webRtcClient != null) {
            ControlMessage msg = ControlMessage.newBuilder()
                    .setAction(Action.MOTION_EVENT)
                    .setMotionAction(action)
                    .setX(relX)
                    .setY(relY)
                    .build();
            webRtcClient.sendControlCommand(msg);
        }
    }

    private void disconnect() {
        if (webRtcClient != null) {
            webRtcClient.close();
            webRtcClient = null;
        }
        if (wsClient != null) {
            wsClient.disconnect();
            wsClient = null;
        }
        // 重置网速统计基线，避免下次连接首帧显示错误速率。
        prevBytesReceived = 0;
        prevBytesSent = 0;
        prevStatsTime = 0;
        connectionStartTime = 0;
        remoteVideoView.clearImage();
        updateUI(false);
        tvStatus.setText("状态: 已停止");
    }

    /** 分辨率预设（发送给被控端的 SET_RESOLUTION 参数）。 */
    private static class ResolutionPreset {
        final String label;
        final int width;
        final int height;
        final int fps;

        ResolutionPreset(String label, int width, int height, int fps) {
            this.label = label;
            this.width = width;
            this.height = height;
            this.fps = fps;
        }

        @Override
        public String toString() {
            return label;
        }
    }

    /** 构建分辨率下拉框，选择后请求被控端切换采集分辨率。 */
    private void setupResolutionSpinner() {
        List<ResolutionPreset> presets = new ArrayList<>();
        presets.add(new ResolutionPreset("原始画质", 0, 0, 0));   // 被控端原生分辨率
        presets.add(new ResolutionPreset("1080P", 1920, 0, 0));
        presets.add(new ResolutionPreset("720P", 1280, 0, 0));
        presets.add(new ResolutionPreset("480P", 854, 0, 0));

        ArrayAdapter<ResolutionPreset> adapter = new ArrayAdapter<>(
                this, android.R.layout.simple_spinner_item, presets);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinnerResolution.setAdapter(adapter);
        spinnerResolution.setSelection(0);

        // 先选中默认项再设置监听，避免初始化即触发一次切换。
        spinnerResolution.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                ResolutionPreset preset = (ResolutionPreset) parent.getItemAtPosition(position);
                if (webRtcClient != null) {
                    webRtcClient.requestResolutionChange(preset.width, preset.height, preset.fps);
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
    }

    /** 构建帧率下拉框；档位以被控端上报（REPORT_RESOLUTION.supported_fps）为准，未上报前使用常用档位。 */
    private void setupFpsSpinner() {
        List<Integer> defaults = new ArrayList<>();
        defaults.add(15);
        defaults.add(24);
        defaults.add(30);
        defaults.add(60);
        applyFpsOptions(defaults, 0);

        spinnerFps.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (position < 0 || position >= fpsOptions.size()) return;
                int fps = fpsOptions.get(position);
                // 与被控端当前帧率一致时不重复发送（含上报同步 setSelection 触发的回调）
                if (fps <= 0 || fps == lastKnownFps || webRtcClient == null) return;
                // 仅切换帧率：分辨率沿用被控端最近上报的实际采集尺寸
                webRtcClient.requestResolutionChange(lastReportedWidth, lastReportedHeight, fps);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });
    }

    /** 更新帧率档位并把选中项同步为最接近 currentFps 的档位。 */
    private void applyFpsOptions(List<Integer> options, int currentFps) {
        if (spinnerFps == null) return;
        if (options != null && !options.isEmpty() && !options.equals(fpsOptions)) {
            fpsOptions.clear();
            fpsOptions.addAll(options);
            List<String> labels = new ArrayList<>();
            for (int f : fpsOptions) labels.add(f + "fps");
            ArrayAdapter<String> adapter = new ArrayAdapter<>(
                    this, android.R.layout.simple_spinner_item, labels);
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            spinnerFps.setAdapter(adapter);
        }
        if (currentFps > 0 && !fpsOptions.isEmpty()) {
            int idx = 0;
            int best = Integer.MAX_VALUE;
            for (int i = 0; i < fpsOptions.size(); i++) {
                int diff = Math.abs(fpsOptions.get(i) - currentFps);
                if (diff < best) {
                    best = diff;
                    idx = i;
                }
            }
            if (spinnerFps.getSelectedItemPosition() != idx) {
                spinnerFps.setSelection(idx);
            }
        }
    }

    private void updateUI(boolean connected) {
        setupPanel.setVisibility(connected ? View.GONE : View.VISIBLE);
        controlPanel.setVisibility(connected ? View.VISIBLE : View.GONE);
        btnConnect.setEnabled(!connected);
        btnDisconnect.setEnabled(connected);
        if (spinnerResolution != null) {
            spinnerResolution.setEnabled(connected);
        }
        if (spinnerFps != null) {
            spinnerFps.setEnabled(connected);
        }
        if (tvStatusControl != null) {
            tvStatusControl.setText(connected ? "远程控制中" : "未连接");
        }

        if (connected) {
            statsHandler.post(statsRunnable);
            if (switchStreamMode != null) switchStreamMode.setEnabled(true);
            if (switchRecord != null) switchRecord.setEnabled(true);
        } else {
            statsHandler.removeCallbacks(statsRunnable);
            if (switchStreamMode != null) {
                switchStreamMode.setEnabled(false);
                programmaticSwitch = true;
                switchStreamMode.setChecked(false);
                programmaticSwitch = false;
            }
            if (tvStreamMode != null) tvStreamMode.setText("串流: WebRTC");
            if (selfCodecDecoder != null) selfCodecDecoder.release();
            // 断开连接时停止录制
            if (videoRecorder != null && videoRecorder.isRecording()) {
                stopRecording();
            }
            if (switchRecord != null) {
                switchRecord.setEnabled(false);
                programmaticSwitch = true;
                switchRecord.setChecked(false);
                programmaticSwitch = false;
            }
            if (tvRecordStatus != null) tvRecordStatus.setText("未录制");
        }
    }

    /** 初始化"自编码"串流模式的 UI 与解码器。 */
    private void setupSelfCodecUi() {
        switchStreamMode = findViewById(R.id.switch_stream_mode);
        tvStreamMode = findViewById(R.id.tv_stream_mode);
        selfCodecSurface = findViewById(R.id.self_codec_surface);
        selfCodecDecoder = new SelfCodecDecoder();
        selfCodecDecoder.setOnResolutionUpdateListener((w, h) -> adjustSelfCodecSize(w, h));
        surfaceCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (selfCodecDecoder != null) {
                    selfCodecDecoder.setSurface(holder.getSurface());
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
            }
        };
        selfCodecSurface.getHolder().addCallback(surfaceCallback);
        switchStreamMode.setEnabled(false);
        switchStreamMode.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (programmaticSwitch) return;
            applyStreamMode(isChecked);
        });
    }

    /** 初始化"屏幕录制"UI，绑定录制开关并处理保存逻辑。 */
    private void setupRecordUi() {
        switchRecord = findViewById(R.id.switch_record);
        tvRecordStatus = findViewById(R.id.tv_record_status);
        switchRecord.setEnabled(false);

        videoRecorder = new VideoRecorder();
        videoRecorder.setOnRecordingListener(new VideoRecorder.OnRecordingListener() {
            @Override
            public void onStarted() {
                runOnUiThread(() -> tvRecordStatus.setText("录制中"));
            }

            @Override
            public void onStopped(String savedLocation) {
                runOnUiThread(() -> {
                    tvRecordStatus.setText("未录制");
                    Toast.makeText(MainActivity.this,
                            "视频已保存: " + savedLocation, Toast.LENGTH_LONG).show();
                });
            }

            @Override
            public void onStoppedEmpty() {
                runOnUiThread(() -> {
                    tvRecordStatus.setText("未录制");
                    Toast.makeText(MainActivity.this, "本次未录制到画面", Toast.LENGTH_SHORT).show();
                });
            }

            @Override
            public void onError(String message) {
                runOnUiThread(() -> {
                    tvRecordStatus.setText("录制失败");
                    programmaticSwitch = true;
                    switchRecord.setChecked(false);
                    programmaticSwitch = false;
                    Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();
                });
            }
        });

        switchRecord.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (programmaticSwitch) return;
            if (isChecked) {
                startRecording();
            } else {
                stopRecording();
            }
        });
    }

    /** 开始录制：先校验存储权限，必要时切回 WebRTC 模式再开始。 */
    private void startRecording() {
        if (!hasStoragePermission()) {
            pendingRecordStart = true;
            requestStoragePermission();
            return;
        }
        doStartRecording();
    }

    private void doStartRecording() {
        if (videoRecorder == null) return;
        videoRecorder.start(this);
        if (webRtcClient != null) {
            webRtcClient.setRecorder(videoRecorder);
            webRtcClient.setRecording(true);
        }
        // 自编码模式下没有 WebRTC 媒体轨道，自动切回 WebRTC 才能录到画面
        if (isSelfCodecMode) {
            applyStreamMode(false);
        }
        tvRecordStatus.setText("录制中");
    }

    private void stopRecording() {
        if (webRtcClient != null) {
            webRtcClient.setRecording(false);
        }
        if (videoRecorder != null) {
            videoRecorder.stop();
        }
        tvRecordStatus.setText("保存中...");
    }

    /** Android 10+ 使用作用域存储，写入应用自身的媒体集合无需存储权限。 */
    private boolean hasStoragePermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            return true;
        }
        return ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED;
    }

    private void requestStoragePermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) return;
        String[] perms;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            perms = new String[]{
                    Manifest.permission.READ_MEDIA_VIDEO,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            };
        } else {
            perms = new String[]{
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE
            };
        }
        ActivityCompat.requestPermissions(this, perms, REQ_RECORD_PERMISSION);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQ_RECORD_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if (pendingRecordStart) {
                    pendingRecordStart = false;
                    doStartRecording();
                }
            } else {
                pendingRecordStart = false;
                programmaticSwitch = true;
                switchRecord.setChecked(false);
                programmaticSwitch = false;
                Toast.makeText(this, "需要存储权限才能保存录制视频", Toast.LENGTH_SHORT).show();
            }
        }
    }

    /** 用户切换串流模式：发送信令并切换本地视图。 */
    private void applyStreamMode(boolean self) {
        if (webRtcClient != null && webRtcClient.isDataChannelOpen()) {
            webRtcClient.sendStreamMode(self ? WebRtcClient.STREAM_MODE_SELF_CODEC
                    : WebRtcClient.STREAM_MODE_WEBRTC);
        }
        tvStreamMode.setText(self ? "串流: 自编码" : "串流: WebRTC");
        if (self) {
            selfCodecSurface.setVisibility(View.VISIBLE);
            remoteVideoView.setVisibility(View.GONE);
            selfCodecDecoder.setEnabled(true);
            if (selfCodecDecoder.getVideoWidth() > 0) {
                adjustSelfCodecSize(selfCodecDecoder.getVideoWidth(), selfCodecDecoder.getVideoHeight());
            }
        } else {
            selfCodecSurface.setVisibility(View.GONE);
            remoteVideoView.setVisibility(View.VISIBLE);
            selfCodecDecoder.setEnabled(false);
        }
    }

    /** 收到被控端回报的当前模式：同步 UI 与视图（防止切换失败不一致）。 */
    private void syncStreamModeUi(int mode) {
        boolean self = (mode == WebRtcClient.STREAM_MODE_SELF_CODEC);
        this.isSelfCodecMode = self;
        programmaticSwitch = true;
        switchStreamMode.setChecked(self);
        programmaticSwitch = false;
        tvStreamMode.setText(self ? "串流: 自编码" : "串流: WebRTC");
        if (self) {
            selfCodecSurface.setVisibility(View.VISIBLE);
            remoteVideoView.setVisibility(View.GONE);
            selfCodecDecoder.setEnabled(true);
            if (selfCodecDecoder.getVideoWidth() > 0) {
                adjustSelfCodecSize(selfCodecDecoder.getVideoWidth(), selfCodecDecoder.getVideoHeight());
            }
        } else {
            selfCodecSurface.setVisibility(View.GONE);
            remoteVideoView.setVisibility(View.VISIBLE);
            selfCodecDecoder.setEnabled(false);
        }
    }

    private void updateStats() {
        if (webRtcClient != null) {
            webRtcClient.getStats(report -> {
                long width = 0, height = 0, fps = 0, delay = 0;
                String codecName = "-";
                double avgDecodeTimeMs = 0;

                // 网速 / 连接类型相关
                long bytesReceived = 0;
                long bytesSent = 0;
                String localCandidateId = null;
                String remoteCandidateId = null;
                boolean hasNominatedPair = false;

                // 抖动 / 丢包 / 丢帧
                double jitterMs = 0;
                String lossRate = "-";
                long framesDropped = 0;

                // 控制 DataChannel 收发消息数
                long dcMessagesSent = 0;
                long dcMessagesReceived = 0;

                for (org.webrtc.RTCStats stats : report.getStatsMap().values()) {
                    String type = stats.getType();
                    if ("inbound-rtp".equals(type) && "video".equals(stats.getMembers().get("kind"))) {
                        // 分辨率
                        Object w = stats.getMembers().get("frameWidth");
                        if (w instanceof Number) width = ((Number) w).longValue();
                        Object h = stats.getMembers().get("frameHeight");
                        if (h instanceof Number) height = ((Number) h).longValue();

                        // 帧率
                        Object f = stats.getMembers().get("framesPerSecond");
                        if (f instanceof Number) fps = ((Number) f).longValue();

                        // 解码耗时 (平均)
                        Object totalTime = stats.getMembers().get("totalDecodeTime");
                        Object frames = stats.getMembers().get("framesDecoded");
                        if (totalTime instanceof Number && frames instanceof Number) {
                            double t = ((Number) totalTime).doubleValue();
                            long c = ((Number) frames).longValue();
                            if (c > 0) {
                                avgDecodeTimeMs = (t * 1000.0) / c;
                            }
                        }

                        // 解码格式
                        Object codecId = stats.getMembers().get("codecId");
                        if (codecId instanceof String) {
                            org.webrtc.RTCStats codecStats = report.getStatsMap().get(codecId);
                            if (codecStats != null) {
                                Object mime = codecStats.getMembers().get("mimeType");
                                if (mime instanceof String) {
                                    codecName = (String) mime;
                                    if (codecName.startsWith("video/")) {
                                        codecName = codecName.substring(6);
                                    }
                                }
                            }
                        }

                        // 解码器实现
                        Object decoder = stats.getMembers().get("decoderImplementation");
                        if (decoder instanceof String) {
                            String d = (String) decoder;
                            codecName = codecName.equals("-") ? d : codecName + " (" + d + ")";
                        }

                        // 抖动（秒 -> 毫秒）
                        Object jitter = stats.getMembers().get("jitter");
                        if (jitter instanceof Number) {
                            jitterMs = ((Number) jitter).doubleValue() * 1000;
                        }

                        // 丢包率
                        Object pr = stats.getMembers().get("packetsReceived");
                        Object pl = stats.getMembers().get("packetsLost");
                        if (pr instanceof Number && pl instanceof Number) {
                            long total = ((Number) pr).longValue() + ((Number) pl).longValue();
                            lossRate = total > 0
                                    ? String.format(Locale.getDefault(), "%.1f", ((Number) pl).doubleValue() / total * 100)
                                    : "0.0";
                        }

                        // 丢帧
                        Object fd = stats.getMembers().get("framesDropped");
                        if (fd instanceof Number) framesDropped = ((Number) fd).longValue();
                    }
                    if ("candidate-pair".equals(type)) {
                        Object nominated = stats.getMembers().get("nominated");
                        if (Boolean.TRUE.equals(nominated)) {
                            hasNominatedPair = true;
                            Object rtt = stats.getMembers().get("currentRoundTripTime");
                            if (rtt instanceof Number) {
                                delay = (long) (((Number) rtt).doubleValue() * 1000);
                            }
                            // candidate-pair 的 bytesReceived/bytesSent 为整条连接
                            // （视频 + DataChannel + ICE）的累计值，用于计算每秒网速。
                            Object br = stats.getMembers().get("bytesReceived");
                            if (br instanceof Number) bytesReceived = ((Number) br).longValue();
                            Object bs = stats.getMembers().get("bytesSent");
                            if (bs instanceof Number) bytesSent = ((Number) bs).longValue();
                            Object lc = stats.getMembers().get("localCandidateId");
                            if (lc instanceof String) localCandidateId = (String) lc;
                            Object rc = stats.getMembers().get("remoteCandidateId");
                            if (rc instanceof String) remoteCandidateId = (String) rc;
                        }
                    }
                    if ("data-channel".equals(type)) {
                        // 仅统计与对端的控制通道
                        Object label = stats.getMembers().get("label");
                        if ("control_channel".equals(label)) {
                            Object ms = stats.getMembers().get("messagesSent");
                            if (ms instanceof Number) dcMessagesSent = ((Number) ms).longValue();
                            Object mr = stats.getMembers().get("messagesReceived");
                            if (mr instanceof Number)
                                dcMessagesReceived = ((Number) mr).longValue();
                        }
                    }
                }

                // 计算每秒上下行网速（与上次采样差值）。
                long now = System.currentTimeMillis();
                String downSpeed = "-";
                String upSpeed = "-";
                if (prevStatsTime != 0) {
                    double dt = (now - prevStatsTime) / 1000.0;
                    if (dt > 0) {
                        double downBps = (bytesReceived - prevBytesReceived) / dt;
                        double upBps = (bytesSent - prevBytesSent) / dt;
                        downSpeed = formatSpeed(downBps);
                        upSpeed = formatSpeed(upBps);
                    }
                }
                prevBytesReceived = bytesReceived;
                prevBytesSent = bytesSent;
                prevStatsTime = now;

                // 判定连接类型与传输协议：两端候选任一为 relay 即走 TURN 中继，否则为 P2P 直连。
                String connType = "-";
                String protocol = "-";
                if (hasNominatedPair) {
                    connType = resolveConnectionType(report, localCandidateId, remoteCandidateId);
                    protocol = resolveProtocol(report, localCandidateId, remoteCandidateId);
                }

                // 连接时长（mm:ss）
                String duration = "-";
                if (connectionStartTime != 0) {
                    long secs = (now - connectionStartTime) / 1000;
                    duration = String.format(Locale.getDefault(), "%02d:%02d", secs / 60, secs % 60);
                }

                // 控制 DataChannel 状态
                String dcStatus = (webRtcClient != null && webRtcClient.isDataChannelOpen())
                        ? "已连接" : "未连接";

                final String statsText = String.format(Locale.getDefault(),
                        "分辨率: %dx%d  帧率: %d  延迟: %dms\n"
                                + "解码格式: %s  抖动: %.0fms  丢包: %s%%  解码耗时: %.1fms\n"
                                + "丢帧: %d  ↓下载: %s  ↑上传: %s\n"
                                + "连接类型: %s (%s)  时长: %s\n"
                                + "控制通道: %s  收发: %d/%d",
                        width, height, fps, delay, codecName, jitterMs, lossRate, avgDecodeTimeMs,
                        framesDropped, downSpeed, upSpeed, connType, protocol,
                        duration, dcStatus, dcMessagesSent, dcMessagesReceived);
                runOnUiThread(() -> {
                    if (tvStats != null) {
                        tvStats.setText(statsText);
                    }
                });
            });
        }
    }

    // 依据选定候选对的本地/远端候选类型，返回 P2P / TURN 中继描述。
    private String resolveConnectionType(org.webrtc.RTCStatsReport report,
                                         String localCandidateId,
                                         String remoteCandidateId) {
        String local = candidateTypeOf(report, localCandidateId);
        String remote = candidateTypeOf(report, remoteCandidateId);
        boolean isRelay = "relay".equals(local) || "relay".equals(remote);
        if (isRelay) {
            if ("relay".equals(local)) return "TURN 中继 (本端经服务器)";
            return "TURN 中继";
        }
        // host: 同一局域网直连; srflx/prflx: 经 NAT 打洞的 P2P。
        return "P2P 直连";
    }

    // 返回选定候选对的传输协议（udp/tcp）。
    private String resolveProtocol(org.webrtc.RTCStatsReport report,
                                   String localCandidateId,
                                   String remoteCandidateId) {
        String protoOf = candidateMemberOf(report, localCandidateId, "protocol");
        String protoOfRemote = candidateMemberOf(report, remoteCandidateId, "protocol");
        if (!"-".equals(protoOf) && !"-".equals(protoOfRemote) && !protoOf.equals(protoOfRemote)) {
            return protoOf + "/" + protoOfRemote;
        }
        return !"-".equals(protoOf) ? protoOf : protoOfRemote;
    }

    private String candidateTypeOf(org.webrtc.RTCStatsReport report, String candidateId) {
        return candidateMemberOf(report, candidateId, "candidateType");
    }

    private String candidateMemberOf(org.webrtc.RTCStatsReport report, String candidateId, String key) {
        if (candidateId == null) return "-";
        org.webrtc.RTCStats candidate = report.getStatsMap().get(candidateId);
        if (candidate == null) return "-";
        Object value = candidate.getMembers().get(key);
        return value instanceof String ? (String) value : "-";
    }

    // 将字节/秒格式化为人类可读字符串（B/s、KB/s、MB/s）。
    private String formatSpeed(double bytesPerSecond) {
        if (bytesPerSecond >= 1024 * 1024) {
            return String.format(Locale.getDefault(), "%.1f MB/s", bytesPerSecond / (1024 * 1024));
        } else if (bytesPerSecond >= 1024) {
            return String.format(Locale.getDefault(), "%.1f KB/s", bytesPerSecond / 1024);
        }
        return String.format(Locale.getDefault(), "%.0f B/s", bytesPerSecond);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        disconnect();
        if (eglBase != null) {
            eglBase.release();
        }
    }
}
