package com.amazon.ads.apidemo.fragments;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.MediaController;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.amazon.ads.apidemo.HttpStatusCode;
import com.amazon.ads.apidemo.R;
import com.amazon.ads.apidemo.utils.L;
import com.amazon.ads.ema.api.model.ApiResponse;
import com.amazon.ads.ema.api.model.HdmiData;
import com.amazon.ads.ema.api.model.HdmiStatus;
import com.amazon.ads.ema.api.model.ScreenRecordChunk;
import com.amazon.ads.ema.api.model.ScreenRecordConfig;
import com.amazon.ads.ema.api.model.ScreenRecordStatus;
import com.amazon.ads.ema.api.model.ScreenshotData;
import com.amazon.ads.ema.api.model.ScreenshotChunk;
import com.amazon.ads.ema.api.model.ScreenshotResult;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * Fragment for display settings and HDMI CEC control
 */
public class DisplayFragment extends BaseFragment {
    private TextView mTvHdmiStatus;
    private ScrollView mScrollView;
    private Button mBtnGetHdmiStatus;
    private Button mBtnControlHdmiPower;
    private Button mBtnVolumeUp;
    private Button mBtnVolumeDown;
    private Button mBtnToggleDisplayPower;
    private Button mBtnToggleMute;
    private Button mBtnCaptureScreenshot;
    private Spinner mSpinnerScreenshotFormat;
    private EditText mEtQuality;
    private EditText mEtMaxWidth;
    private TextView mTvScreenshotInfo;
    private ImageView mIvScreenshot;

    // Screen recording views
    private Button mBtnRecordScreen;
    private EditText mEtRecordDuration;
    private Spinner mSpinnerResolution;
    private VideoView mVvRecording;
    private TextView mTvRecordInfo;

    // Floating countdown overlay (from MainActivity)
    private View mCountdownOverlay;
    private TextView mTvCountdownOverlay;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_display, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        initializeBase();

        // Initialize views
        mScrollView = view.findViewById(R.id.scroll_view_display);
        mBtnGetHdmiStatus = view.findViewById(R.id.btn_get_hdmi_status);
        mBtnControlHdmiPower = view.findViewById(R.id.btn_control_hdmi_power);
        mBtnVolumeUp = view.findViewById(R.id.btn_volume_up);
        mBtnVolumeDown = view.findViewById(R.id.btn_volume_down);
        mBtnToggleDisplayPower = view.findViewById(R.id.btn_toggle_display_power);
        mBtnToggleMute = view.findViewById(R.id.btn_toggle_mute);
        mBtnCaptureScreenshot = view.findViewById(R.id.btn_capture_screenshot);
        mSpinnerScreenshotFormat = view.findViewById(R.id.spinner_screenshot_format);
        mEtQuality = view.findViewById(R.id.et_screenshot_quality);
        mEtMaxWidth = view.findViewById(R.id.et_screenshot_max_width);

        mTvHdmiStatus = view.findViewById(R.id.tv_hdmi_status);
        mTvScreenshotInfo = view.findViewById(R.id.tv_screenshot_info);
        mIvScreenshot = view.findViewById(R.id.iv_screenshot);

        // Screen recording views
        mBtnRecordScreen = view.findViewById(R.id.btn_record_screen);
        mEtRecordDuration = view.findViewById(R.id.et_record_duration);
        mSpinnerResolution = view.findViewById(R.id.spinner_resolution);
        mVvRecording = view.findViewById(R.id.vv_recording);
        mTvRecordInfo = view.findViewById(R.id.tv_record_info);

        // Inline validation for recording duration: surface the 1-20s limit as
        // the user types instead of only erroring when Record is clicked.
        mEtRecordDuration.addTextChangedListener(new android.text.TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {}
            @Override
            public void afterTextChanged(android.text.Editable s) {
                String text = s.toString().trim();
                if (text.isEmpty()) {
                    mEtRecordDuration.setError(null);
                    return;
                }
                try {
                    int d = Integer.parseInt(text);
                    mEtRecordDuration.setError(d < 1 || d > 20 ? "Duration must be 1-20 seconds" : null);
                } catch (NumberFormatException e) {
                    mEtRecordDuration.setError("Enter a number 1-20");
                }
            }
        });

        // Get floating countdown overlay from MainActivity
        if (getActivity() != null) {
            mCountdownOverlay = getActivity().findViewById(R.id.countdown_overlay);
            mTvCountdownOverlay = getActivity().findViewById(R.id.tv_countdown_overlay);
        }

        // Setup resolution spinner
        ArrayAdapter<String> resolutionAdapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item,
                new String[]{"native", "720p", "1080p"});
        resolutionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mSpinnerResolution.setAdapter(resolutionAdapter);

        // Setup screenshot format spinner
        ArrayAdapter<String> screenshotFormatAdapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item,
                new String[]{"JPEG", "PNG", "WEBP"});
        screenshotFormatAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mSpinnerScreenshotFormat.setAdapter(screenshotFormatAdapter);

        // Restore focus after spinner selection to prevent focus loss with dpad
        android.widget.AdapterView.OnItemSelectedListener focusRestoreListener = new android.widget.AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(android.widget.AdapterView<?> parent, View v, int position, long id) {
                // Move focus to the next actionable button
                if (parent == mSpinnerScreenshotFormat) {
                    mBtnCaptureScreenshot.requestFocus();
                } else if (parent == mSpinnerResolution) {
                    mBtnRecordScreen.requestFocus();
                }
            }
            @Override
            public void onNothingSelected(android.widget.AdapterView<?> parent) {}
        };
        mSpinnerScreenshotFormat.setOnItemSelectedListener(focusRestoreListener);
        mSpinnerResolution.setOnItemSelectedListener(focusRestoreListener);

        // Set click listeners
        mBtnGetHdmiStatus.setOnClickListener(v -> getHdmiStatus());
        mBtnControlHdmiPower.setOnClickListener(v -> controlHdmiPower());
        mBtnVolumeUp.setOnClickListener(v -> adjustVolume(true));
        mBtnVolumeDown.setOnClickListener(v -> adjustVolume(false));
        mBtnToggleDisplayPower.setOnClickListener(v -> toggleDisplayPower());
        mBtnToggleMute.setOnClickListener(v -> toggleMute());
        mBtnCaptureScreenshot.setOnClickListener(v -> captureScreenshot());
        mBtnRecordScreen.setOnClickListener(v -> recordScreen());

        // Setup keyboard shortcuts
        setupKeyboardShortcuts(view);

        mBtnGetHdmiStatus.requestFocus();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getView() != null) {
            getView().requestFocus();
        }
    }

    private void setupKeyboardShortcuts(View view) {
        if (mScrollView != null) {
            mScrollView.setFocusable(true);
            mScrollView.setFocusableInTouchMode(true);
            mScrollView.setOnKeyListener((v, keyCode, event) -> {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    switch (keyCode) {
                    case KeyEvent.KEYCODE_DPAD_UP:
                        mScrollView.smoothScrollBy(0, -200);
                        return true;
                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        mScrollView.smoothScrollBy(0, 200);
                        return true;
                    }
                }
                return false;
            });
        }
    }

    /**
     * Public methods to trigger actions from MainActivity
     */
    public void triggerGetHdmiStatus() {
        L.i("DisplayFragment triggerGetHdmiStatus() called from MainActivity");
        showButtonPressEffect(mBtnGetHdmiStatus);
        getHdmiStatus();
    }

    public void triggerControlHdmiPower() {
        L.i("DisplayFragment triggerControlHdmiPower() called from MainActivity");
        showButtonPressEffect(mBtnControlHdmiPower);
        controlHdmiPower();
    }

    public void triggerVolumeDown() {
        L.i("DisplayFragment triggerVolumeDown() called from MainActivity");
        showButtonPressEffect(mBtnVolumeDown);
        adjustVolume(false);
    }

    public void triggerVolumeUp() {
        L.i("DisplayFragment triggerVolumeUp() called from MainActivity");
        showButtonPressEffect(mBtnVolumeUp);
        adjustVolume(true);
    }

    public void triggerCaptureScreenshot() {
        L.i("DisplayFragment triggerCaptureScreenshot() called from MainActivity");
        showButtonPressEffect(mBtnCaptureScreenshot);
        captureScreenshot();
    }

    public void triggerCaptureScreenshotWebp() {
        L.i("DisplayFragment triggerCaptureScreenshotWebp() called from MainActivity");
        // Set spinner to WEBP and capture
        mSpinnerScreenshotFormat.setSelection(2); // WEBP is at index 2
        showButtonPressEffect(mBtnCaptureScreenshot);
        captureScreenshot();
    }

    public void triggerToggleDisplayPower() {
        L.i("DisplayFragment triggerToggleDisplayPower() called from MainActivity");
        showButtonPressEffect(mBtnToggleDisplayPower);
        toggleDisplayPower();
    }

    public void triggerToggleMute() {
        L.i("DisplayFragment triggerToggleMute() called from MainActivity");
        showButtonPressEffect(mBtnToggleMute);
        toggleMute();
    }

    public void triggerRecordScreen() {
        L.i("DisplayFragment triggerRecordScreen() called from MainActivity");
        showButtonPressEffect(mBtnRecordScreen);
        recordScreen();
    }

    private void recordScreen() {
        if (!checkConnection())
            return;

        // Parse duration from input
        int duration = 5;
        try {
            duration = Integer.parseInt(mEtRecordDuration.getText().toString().trim());
            if (duration < 1 || duration > 20) {
                Toast.makeText(getContext(), "Duration must be 1-20 seconds", Toast.LENGTH_SHORT).show();
                return;
            }
        } catch (NumberFormatException e) {
            Toast.makeText(getContext(), "Invalid duration", Toast.LENGTH_SHORT).show();
            return;
        }

        String resolution = mSpinnerResolution.getSelectedItem().toString();
        final int finalDuration = duration;

        updateLastApiCall("Starting screen recording for " + duration + "s (" + resolution + ")...");
        mBtnRecordScreen.setEnabled(false);
        mBtnRecordScreen.setText("Starting...");

        new Thread(() -> {
            // Step 1: Start recording
            ScreenRecordConfig config = new ScreenRecordConfig();
            config.durationSeconds = finalDuration;
            config.resolution = resolution;
            config.bitrateMbps = 4;

            ScreenRecordStatus startResponse = mApiClient.startScreenRecord(config);

            if (startResponse.code != HttpStatusCode.OK_200) {
                mHandler.post(() -> {
                    mBtnRecordScreen.setEnabled(true);
                    mBtnRecordScreen.setText("Record Screen");
                    showError("Failed to start recording", startResponse.message);
                });
                return;
            }

            // Show countdown and start recording UI
            mHandler.post(() -> {
                mBtnRecordScreen.setText("Recording...");
                mTvRecordInfo.setText("Recording in progress...");
                mTvRecordInfo.setVisibility(View.VISIBLE);

                // Show floating countdown overlay
                if (mCountdownOverlay != null && mTvCountdownOverlay != null) {
                    mTvCountdownOverlay.setText(String.valueOf(finalDuration));
                    mCountdownOverlay.setVisibility(View.VISIBLE);
                }
            });

            // Step 2: Countdown and poll for recording completion
            long startTime = System.currentTimeMillis();
            int lastCountdown = finalDuration;

            while (true) {
                try {
                    Thread.sleep(100); // Check more frequently for smoother countdown
                } catch (InterruptedException e) {
                    break;
                }

                long elapsedMs = System.currentTimeMillis() - startTime;
                int remainingSeconds = finalDuration - (int) (elapsedMs / 1000);

                // Update countdown display
                if (remainingSeconds != lastCountdown && remainingSeconds >= 0) {
                    lastCountdown = remainingSeconds;
                    final int countdown = remainingSeconds;
                    mHandler.post(() -> {
                        if (mTvCountdownOverlay != null) {
                            if (countdown > 0) {
                                mTvCountdownOverlay.setText(String.valueOf(countdown));
                            } else {
                                mTvCountdownOverlay.setText("✓");
                            }
                        }
                    });
                }

                // Check recording status periodically
                if (elapsedMs % 500 < 100) { // Check every ~500ms
                    ScreenRecordStatus status = mApiClient.getScreenRecordStatus();

                    if ("completed".equals(status.state)) {
                        L.i("Recording completed, starting download...");
                        break;
                    } else if ("failed".equals(status.state)) {
                        mHandler.post(() -> {
                            // Hide floating countdown overlay
                            if (mCountdownOverlay != null) {
                                mCountdownOverlay.setVisibility(View.GONE);
                            }
                            mBtnRecordScreen.setEnabled(true);
                            mBtnRecordScreen.setText("Record Screen");
                            showError("Recording failed", status.message);
                        });
                        return;
                    }
                }

                // Timeout check
                if (elapsedMs > (finalDuration + 5) * 1000) {
                    L.w("Recording timeout, checking final status...");
                    ScreenRecordStatus status = mApiClient.getScreenRecordStatus();
                    if ("completed".equals(status.state)) {
                        break;
                    }
                    mHandler.post(() -> {
                        // Hide floating countdown overlay
                        if (mCountdownOverlay != null) {
                            mCountdownOverlay.setVisibility(View.GONE);
                        }
                        mBtnRecordScreen.setEnabled(true);
                        mBtnRecordScreen.setText("Record Screen");
                        showError("Recording timeout", "Recording did not complete in expected time");
                    });
                    return;
                }
            }

            // Step 3: Download video in chunks
            mHandler.post(() -> {
                // Hide floating countdown overlay
                if (mCountdownOverlay != null) {
                    mCountdownOverlay.setVisibility(View.GONE);
                }
                mBtnRecordScreen.setText("Downloading...");
                mTvRecordInfo.setText("Downloading video...");
            });

            File outputFile = getLocalVideoFile();
            L.i("[record] downloading mp4 to: " + outputFile.getAbsolutePath());
            boolean downloadSuccess = downloadVideoChunks(outputFile);

            if (!downloadSuccess) {
                L.e("[record] download FAILED, partial file at: "
                        + outputFile.getAbsolutePath()
                        + " size=" + (outputFile.exists() ? outputFile.length() : 0));
                mHandler.post(() -> {
                    mBtnRecordScreen.setEnabled(true);
                    mBtnRecordScreen.setText("Record Screen");
                    showError("Failed to download video", "Error during chunk transfer");
                });
                return;
            }
            L.i("[record] download OK: " + outputFile.getAbsolutePath()
                    + " size=" + outputFile.length() + " bytes");

            // Step 4: Clear remote recording
            mApiClient.clearScreenRecord();

            // Step 5: Play the video
            mHandler.post(() -> {
                mBtnRecordScreen.setEnabled(true);
                mBtnRecordScreen.setText("Record Screen");

                if (outputFile.exists() && outputFile.length() > 0) {
                    // Display the video
                    mVvRecording.setVideoURI(Uri.fromFile(outputFile));
                    mVvRecording.setVisibility(View.VISIBLE);

                    MediaController mediaController = new MediaController(getContext());
                    mediaController.setAnchorView(mVvRecording);
                    mVvRecording.setMediaController(mediaController);
                    mVvRecording.start();

                    // Format and display recording info
                    String info = formatLocalRecordInfo(outputFile, finalDuration, resolution);
                    mTvRecordInfo.setText(info);
                    mTvRecordInfo.setVisibility(View.VISIBLE);

                    Toast.makeText(getContext(), "Recording completed", Toast.LENGTH_SHORT).show();
                    updateLastApiCall("Screen recording completed.");

                    // Scroll to show the video and focus it
                    mHandler.postDelayed(() -> {
                        if (mScrollView != null && mVvRecording != null) {
                            mScrollView.smoothScrollTo(0, mVvRecording.getTop());
                        }
                        mVvRecording.requestFocus();
                    }, 100);
                } else {
                    showError("Recording file not found", outputFile.getAbsolutePath().replace("/storage/emulated/0", "/sdcard"));
                }
            });
        }).start();
    }

    private File getLocalVideoFile() {
        File apiDemoDir = new File(Environment.getExternalStorageDirectory(), "apidemo");
        if (!apiDemoDir.exists()) {
            apiDemoDir.mkdirs();
        }
        String filename = "recording_" + System.currentTimeMillis() + ".mp4";
        return new File(apiDemoDir, filename);
    }

    private boolean downloadVideoChunks(File outputFile) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(outputFile);
            long offset = 0;
            long totalSize = 0;
            int chunkCount = 0;

            while (true) {
                ScreenRecordChunk chunk = mApiClient.getScreenRecordChunk(offset);

                if (chunk.code != HttpStatusCode.OK_200) {
                    L.e("Error getting chunk at offset " + offset + ": " + chunk.message);
                    return false;
                }

                if (chunk.data != null && chunk.chunkSize > 0) {
                    fos.write(chunk.data, 0, chunk.chunkSize);
                    offset += chunk.chunkSize;
                    chunkCount++;
                    totalSize = chunk.totalSize;

                    final long currentOffset = offset;
                    final long total = totalSize;
                    mHandler.post(() -> {
                        int percent = total > 0 ? (int) ((currentOffset * 100) / total) : 0;
                        mTvRecordInfo.setText(
                                "Downloading video...\nProgress: " + percent + "% (" + formatFileSize(currentOffset) + " / " + formatFileSize(total) +
                                        ")");
                    });
                }

                if (chunk.isLastChunk) {
                    L.i("Download complete: " + chunkCount + " chunks, " + offset + " bytes");
                    break;
                }
            }

            fos.flush();
            return true;

        } catch (IOException e) {
            L.e("Error writing video file", e);
            return false;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    L.e("Error closing file", e);
                }
            }
        }
    }

    private String formatLocalRecordInfo(File file, int durationSeconds, String resolution) {
        StringBuilder sb = new StringBuilder();
        sb.append("Recording Info:\n\n");
        sb.append("Format: MP4\n");
        sb.append("Resolution: ").append(resolution).append("\n");
        sb.append("Duration: ").append(durationSeconds).append(" seconds\n");
        sb.append("File Size: ").append(formatFileSize(file.length())).append("\n");
        String filePath = file.getAbsolutePath().replace("/storage/emulated/0", "/sdcard");
        sb.append("File Path: ").append(filePath).append("\n");
        return sb.toString();
    }

    private void captureScreenshot() {
        if (!checkConnection())
            return;

        final String format = mSpinnerScreenshotFormat.getSelectedItem().toString();

        // Parse quality from input (default 100)
        int quality = 100;
        String qualityStr = mEtQuality.getText().toString().trim();
        if (!qualityStr.isEmpty()) {
            try {
                quality = Integer.parseInt(qualityStr);
                if (quality < 1 || quality > 100) {
                    Toast.makeText(getContext(), "Quality must be between 1 and 100", Toast.LENGTH_SHORT).show();
                    return;
                }
            } catch (NumberFormatException e) {
                Toast.makeText(getContext(), "Invalid quality value", Toast.LENGTH_SHORT).show();
                return;
            }
        }
        final int finalQuality = quality;

        // Parse maxWidth from input (0 = full resolution)
        int maxWidth = 0;
        String maxWidthStr = mEtMaxWidth.getText().toString().trim();
        if (!maxWidthStr.isEmpty()) {
            try {
                maxWidth = Integer.parseInt(maxWidthStr);
            } catch (NumberFormatException e) {
                Toast.makeText(getContext(), "Invalid max width value", Toast.LENGTH_SHORT).show();
                return;
            }
        }
        final int finalMaxWidth = maxWidth;

        String desc = maxWidth > 0
                ? "Capturing screenshot (" + format + ", q=" + finalQuality + ", maxWidth=" + maxWidth + ")..."
                : "Capturing screenshot (" + format + ", q=" + finalQuality + ")...";
        updateLastApiCall(desc);

        new Thread(() -> {
            ScreenshotResult response;
            if (finalMaxWidth > 0) {
                response = mApiClient.captureScreenScaled(format, finalQuality, finalMaxWidth);
            } else {
                response = mApiClient.captureScreen(format, finalQuality);
            }

            if (response.code != HttpStatusCode.OK_200 || response.data == null) {
                mHandler.post(() -> showError("Failed to capture screenshot", response.message));
                return;
            }

            // Check if response is chunked (image too large for single Binder transfer)
            byte[] imageData;
            if (response.data.chunked) {
                L.i("Screenshot is chunked (totalSize=" + response.data.totalSize + "), downloading chunks...");
                mHandler.post(() -> updateLastApiCall("Screenshot chunked (" + (response.data.totalSize / 1024) + "KB), downloading chunks..."));
                imageData = downloadScreenshotChunks(response.data.totalSize);
                if (imageData == null) {
                    mHandler.post(() -> showError("Failed to download screenshot chunks", "Chunk transfer failed"));
                    return;
                }
                // Clear server-side cache after successful download
                mApiClient.clearScreenshot();
            } else {
                imageData = response.data.imageData;
            }

            if (imageData == null || imageData.length == 0) {
                mHandler.post(() -> showError("Failed to capture screenshot", "No image data"));
                return;
            }

            final byte[] finalImageData = imageData;
            final ScreenshotData screenshotData = response.data;
            // Fill in imageData for display purposes if it was chunked
            if (screenshotData.imageData == null) {
                screenshotData.imageData = finalImageData;
                screenshotData.fileSizeBytes = finalImageData.length;
            }

            mHandler.post(() -> {
                Bitmap bitmap = BitmapFactory.decodeByteArray(finalImageData, 0, finalImageData.length);
                if (bitmap != null) {
                    File savedFile = saveScreenshotToFile(finalImageData, format);
                    mIvScreenshot.setImageBitmap(bitmap);
                    mIvScreenshot.setVisibility(View.VISIBLE);

                    String info = formatScreenshotInfo(screenshotData, savedFile);
                    mTvScreenshotInfo.setText(info);
                    mTvScreenshotInfo.setVisibility(View.VISIBLE);

                    String mode = screenshotData.chunked ? " chunked" : "";
                    Toast.makeText(getContext(), "Screenshot captured" + mode + " (" + format + ")", Toast.LENGTH_SHORT).show();
                    updateLastApiCall("Screenshot captured successfully" + mode + " (" + format + ", " + (finalImageData.length / 1024) + "KB)");

                    // Scroll to show screenshot and focus the image
                    mHandler.postDelayed(() -> {
                        if (mScrollView != null && mIvScreenshot != null) {
                            mScrollView.smoothScrollTo(0, mIvScreenshot.getTop());
                        }
                        mIvScreenshot.requestFocus();
                    }, 100);
                } else {
                    showError("Failed to decode screenshot", "Unable to convert image data to bitmap");
                }
            });
        }).start();
    }

    /**
     * Download screenshot data in chunks when the image exceeds Binder limit.
     * @param totalSize Expected total size in bytes
     * @return Complete image byte array, or null on failure
     */
    private byte[] downloadScreenshotChunks(long totalSize) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream((int) totalSize);
        long offset = 0;

        while (true) {
            ScreenshotChunk chunk = mApiClient.getScreenshotChunk(offset);
            if (chunk.code != HttpStatusCode.OK_200) {
                L.e("Screenshot chunk download failed at offset", offset, "code:", chunk.code, "msg:", chunk.message);
                return null;
            }

            if (chunk.data != null && chunk.data.length > 0) {
                baos.write(chunk.data, 0, chunk.data.length);
                offset += chunk.data.length;
                L.d("Screenshot chunk: offset=", offset, "/", totalSize,
                        "chunkSize=", chunk.chunkSize, "isLast=", chunk.isLastChunk);
            }

            if (chunk.isLastChunk) {
                break;
            }
        }

        L.i("Screenshot chunks downloaded successfully, total:", baos.size(), "bytes");
        return baos.toByteArray();
    }

    private File saveScreenshotToFile(byte[] imageData, String format) {
        File apidemoDir = new File(Environment.getExternalStorageDirectory(), "apidemo");
        if (!apidemoDir.exists()) {
            apidemoDir.mkdirs();
        }
        String extension;
        if ("PNG".equalsIgnoreCase(format)) {
            extension = "png";
        } else if ("JPEG".equalsIgnoreCase(format)) {
            extension = "jpg";
        } else {
            extension = "webp";
        }
        String filename = "screenshot_" + System.currentTimeMillis() + "." + extension;
        File outputFile = new File(apidemoDir, filename);

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(outputFile);
            fos.write(imageData);
            fos.flush();
            L.i("Screenshot saved to: " + outputFile.getAbsolutePath().replace("/storage/emulated/0", "/sdcard"));
        } catch (IOException e) {
            L.e("Error saving screenshot", e);
            return null;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    L.e("Error closing file", e);
                }
            }
        }
        return outputFile;
    }

    private void adjustVolume(boolean volumeUp) {
        if (!checkConnection())
            return;

        String action = volumeUp ? "up" : "down";
        updateLastApiCall("Adjusting volume " + action + "...");

        new Thread(() -> {
            ApiResponse response = mApiClient.adjustVolume(volumeUp);
            mHandler.post(() -> {
                if (response.code == HttpStatusCode.OK_200) {
                    Toast.makeText(getContext(), "Volume " + action, Toast.LENGTH_SHORT).show();
                    updateLastApiCall("Volume adjusted " + action);
                } else {
                    showError("Failed to adjust volume", response.message);
                }
            });
        }).start();
    }

    private void getHdmiStatus() {
        if (!checkConnection())
            return;

        updateLastApiCall("Getting HDMI CEC status...");

        new Thread(() -> {
            HdmiStatus response = mApiClient.getHdmiStatus();
            mHandler.post(() -> {
                if (response.code == HttpStatusCode.OK_200 && response.data != null) {
                    String hdmiInfo = formatHdmiStatus(response.data);
                    mTvHdmiStatus.setText(hdmiInfo);
                    mTvHdmiStatus.setVisibility(View.VISIBLE);
                    updateLastApiCall("HDMI status retrieved");
                } else {
                    showError("Failed to get HDMI status", response.message);
                }
            });
        }).start();
    }

    private void controlHdmiPower() {
        if (!checkConnection())
            return;

        // Turn HDMI power ON
        updateLastApiCall("Setting HDMI power to on...");

        new Thread(() -> {
            ApiResponse response = mApiClient.setDisplayPower(true);

            mHandler.post(() -> {
                if (response.code == HttpStatusCode.OK_200) {
                    Toast.makeText(getContext(), "HDMI power turned on", Toast.LENGTH_SHORT).show();
                    updateLastApiCall("HDMI power turned on");

                    // Refresh HDMI status after a delay
                    mHandler.postDelayed(() -> {
                        mApiClient.refreshHdmiStatus();
                        mHandler.postDelayed(this::getHdmiStatus, 500);
                    }, 1000);
                } else {
                    showError("Failed to turn on HDMI power", response.message);
                }
            });
        }).start();
    }

    private String formatHdmiStatus(HdmiData data) {
        if (data == null)
            return "No HDMI data available";

        StringBuilder sb = new StringBuilder();
        sb.append("HDMI Status:\n\n");
        sb.append("Connected: ").append(data.connected).append("\n");
        sb.append("CEC Enabled: ").append(data.cecEnabled).append("\n");
        sb.append("Active Source: ").append(data.activeSource).append("\n\n");

        if (data.connected) {
            sb.append("Monitor Info: ").append(data.monitorInfo).append("\n");

            if (data.display != null) {
                sb.append("\nDisplay Details:\n");
                sb.append("  Manufacturer: ").append(data.display.manufacturer).append("\n");
                sb.append("  Model: ").append(data.display.model).append("\n");
                sb.append("  Resolution: ").append(data.display.resolution).append("\n");
                sb.append("  Refresh Rate: ").append(data.display.refreshRate).append(" Hz\n");
            }

            if (data.power != null) {
                sb.append("\nPower Status:\n");
                sb.append("  Display On: ").append(data.power.displayOn).append("\n");
                sb.append("  State: ").append(data.power.state).append("\n");
                sb.append("  Can Control: ").append(data.power.canControl).append("\n");
            }

            if (data.audio != null) {
                sb.append("\nAudio Status:\n");
                sb.append("  Volume: ").append(data.audio.volume).append("\n");
                sb.append("  Muted: ").append(data.audio.muted).append("\n");
                sb.append("  Can Control: ").append(data.audio.canControl).append("\n");
            }
        }

        return sb.toString();
    }

    private String formatScreenshotInfo(ScreenshotData data, File file) {
        if (data == null)
            return "No screenshot data available";

        StringBuilder sb = new StringBuilder();
        sb.append("Screenshot Info:\n\n");
        sb.append("Format: ").append(data.format).append("\n");
        sb.append("Resolution: ").append(data.widthPx).append(" x ").append(data.heightPx).append(" px\n");
        sb.append("File Size: ").append(formatFileSize(data.fileSizeBytes)).append("\n");
        sb.append("Data Size: ").append(data.imageData != null ? data.imageData.length : 0).append(" bytes\n");
        String filePath = file.getAbsolutePath().replace("/storage/emulated/0", "/sdcard");
        sb.append("File Path: ").append(filePath).append("\n");
        return sb.toString();
    }

    private String formatFileSize(long bytes) {
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else {
            return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
        }
    }

    private void toggleDisplayPower() {
        if (!checkConnection())
            return;

        // Turn HDMI power OFF
        updateLastApiCall("Setting HDMI power to off...");

        new Thread(() -> {
            ApiResponse response = mApiClient.setDisplayPower(false);
            mHandler.post(() -> {
                if (response.code == HttpStatusCode.OK_200) {
                    Toast.makeText(getContext(), "HDMI power turned off", Toast.LENGTH_SHORT).show();
                    updateLastApiCall("HDMI power turned off");

                    // Refresh HDMI status after a delay
                    mHandler.postDelayed(() -> {
                        mApiClient.refreshHdmiStatus();
                        mHandler.postDelayed(this::getHdmiStatus, 500);
                    }, 1000);
                } else {
                    showError("Failed to turn off HDMI power", response.message);
                }
            });
        }).start();
    }

    private void toggleMute() {
        if (!checkConnection())
            return;

        updateLastApiCall("Toggling mute...");

        new Thread(() -> {
            ApiResponse response = mApiClient.toggleMute();
            mHandler.post(() -> {
                if (response.code == HttpStatusCode.OK_200) {
                    Toast.makeText(getContext(), "Mute toggled", Toast.LENGTH_SHORT).show();
                    updateLastApiCall("Mute toggled");

                    // Optionally refresh HDMI status to show updated audio state
                    mHandler.postDelayed(this::getHdmiStatus, 500);
                } else {
                    showError("Failed to toggle mute", response.message);
                }
            });
        }).start();
    }
}
