package com.foxx.androidcast;

/*********************************************************************
 * SettingsUi.java
 * Created at: Fri 15 May 2026 16:49:02 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * class SettingsUi
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (7 commits, 289 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	4a7ecb185f2d81d364cd5b3601e95e04607c1d2a1f5a7153e32928d1fba6c731
**********************************************************************/
import android.os.Build;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.Spinner;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.foxx.androidcast.sender.CodecCatalog;

import java.util.ArrayList;
import java.util.List;
/** Binds cast option spinners/checkbox to {@link CastSettings}. */
public final class SettingsUi {
    private SettingsUi() {}

    public static void bindSender(AppCompatActivity activity, CastSettings settings) {
        bindCommon(activity, settings);
        bindCaptureModeSender(activity, settings);
        PlatformCastSupport.bindAudioCheckbox(activity, activity.findViewById(R.id.checkbox_audio), settings);
    }

    public static void bindReceiver(AppCompatActivity activity, CastSettings settings) {
        bindCommon(activity, settings);
        View captureRow = activity.findViewById(R.id.layout_capture_mode);
        if (captureRow != null) {
            captureRow.setVisibility(View.GONE);
        }
        PlatformCastSupport.bindAudioCheckbox(activity, activity.findViewById(R.id.checkbox_audio), settings);
    }

    /** @deprecated use {@link #bindSender} or {@link #bindReceiver} */
    @Deprecated
    public static void bind(AppCompatActivity activity, CastSettings settings) {
        bindSender(activity, settings);
    }

    private static void bindCommon(AppCompatActivity activity, CastSettings settings) {
        PlatformCastSupport.applyDefaultAudioSetting(settings);

        Spinner transport = activity.findViewById(R.id.spinner_transport);
        Spinner videoCodec = activity.findViewById(R.id.spinner_video_codec);
        Spinner adjustment = activity.findViewById(R.id.spinner_adjustment);
        Spinner bitrateMode = activity.findViewById(R.id.spinner_bitrate_mode);
        Spinner quality = activity.findViewById(R.id.spinner_quality);
        Spinner networkAdoption = activity.findViewById(R.id.spinner_network_adoption);
        Spinner resolution = activity.findViewById(R.id.spinner_resolution);

        bindSpinner(activity, transport, new String[]{
                activity.getString(R.string.transport_udp),
                activity.getString(R.string.transport_tcp),
                activity.getString(R.string.transport_quic),
                activity.getString(R.string.transport_webrtc)
        }, position -> {
            switch (position) {
                case 1:
                    settings.setTransport(CastConfig.TRANSPORT_TCP);
                    break;
                case 2:
                    settings.setTransport(CastConfig.TRANSPORT_QUIC);
                    break;
                case 3:
                    settings.setTransport(CastConfig.TRANSPORT_WEBRTC);
                    break;
                case 0:
                default:
                    settings.setTransport(CastConfig.TRANSPORT_UDP);
                    break;
            }
        });
        int transportIndex = 0;
        if (CastConfig.TRANSPORT_TCP.equals(settings.getTransport())) {
            transportIndex = 1;
        } else if (CastConfig.TRANSPORT_QUIC.equals(settings.getTransport())) {
            transportIndex = 2;
        } else if (CastConfig.TRANSPORT_WEBRTC.equals(settings.getTransport())) {
            transportIndex = 3;
        }
        transport.setSelection(transportIndex);

        List<CastSettings.VideoCodec> codecs = CodecCatalog.supportedChoices();
        List<String> codecLabels = new ArrayList<>();
        for (CastSettings.VideoCodec c : codecs) {
            codecLabels.add(labelVideoCodec(activity, c));
        }
        bindSpinner(activity, videoCodec, codecLabels.toArray(new String[0]), position -> {
            if (position >= 0 && position < codecs.size()) {
                settings.setVideoCodec(codecs.get(position));
            }
        });
        selectCodecSpinner(videoCodec, codecs, settings.getVideoCodec());

        bindEnumSpinner(activity, adjustment, CastSettings.AdjustmentPreset.values(),
                SettingsUi::labelAdjustment, settings.getAdjustmentPreset().ordinal(),
                p -> settings.setAdjustmentPreset(CastSettings.AdjustmentPreset.values()[p]));

        bindEnumSpinner(activity, bitrateMode, CastSettings.BitrateMode.values(),
                SettingsUi::labelBitrateMode, settings.getBitrateMode().ordinal(),
                p -> settings.setBitrateMode(CastSettings.BitrateMode.values()[p]));

        bindSpinner(activity, quality, new String[]{
                activity.getString(R.string.quality_low),
                activity.getString(R.string.quality_medium),
                activity.getString(R.string.quality_high)
        }, position -> settings.setQuality(CastSettings.Quality.values()[position]));
        quality.setSelection(CastSettings.Quality.MEDIUM.ordinal());

        bindEnumSpinner(activity, networkAdoption, CastSettings.NetworkAdoption.values(),
                SettingsUi::labelNetworkAdoption, settings.getNetworkAdoption().ordinal(),
                p -> settings.setNetworkAdoption(CastSettings.NetworkAdoption.values()[p]));

        String[] resolutions = {
                activity.getString(R.string.resolution_full),
                activity.getString(R.string.resolution_75),
                activity.getString(R.string.resolution_half),
                activity.getString(R.string.resolution_720p),
                activity.getString(R.string.resolution_fixed_1080p),
                activity.getString(R.string.resolution_fixed_720p),
                activity.getString(R.string.resolution_adaptive)
        };
        bindSpinner(activity, resolution, resolutions,
                position -> settings.setResolution(CastSettings.Resolution.values()[position]));
        resolution.setSelection(CastSettings.Resolution.HD_720P.ordinal());
    }

    private static void bindCaptureModeSender(AppCompatActivity activity, CastSettings settings) {
        Spinner captureMode = activity.findViewById(R.id.spinner_capture_mode);
        TextView label = activity.findViewById(R.id.text_capture_mode_label);
        if (captureMode == null) {
            return;
        }
        java.util.List<String> modes = new java.util.ArrayList<>();
        java.util.List<CastSettings.CaptureMode> modeValues = new java.util.ArrayList<>();
        modes.add(activity.getString(R.string.capture_full_screen));
        modeValues.add(CastSettings.CaptureMode.FULL_SCREEN);
        if (PlatformCastSupport.isCaptureUserChoiceSupported()) {
            modes.add(activity.getString(R.string.capture_user_choice));
            modeValues.add(CastSettings.CaptureMode.USER_CHOICE);
        } else {
            modes.add(activity.getString(R.string.capture_user_choice_unavailable, Build.VERSION.SDK_INT));
            modeValues.add(CastSettings.CaptureMode.FULL_SCREEN);
        }
        modes.add(activity.getString(R.string.capture_calibration_test));
        modeValues.add(CastSettings.CaptureMode.CALIBRATION_TEST);
        if (PlatformCastSupport.isCameraCaptureModeSupported()) {
            modes.add(activity.getString(R.string.capture_camera));
            modeValues.add(CastSettings.CaptureMode.CAMERA);
        } else {
            modes.add(activity.getString(R.string.capture_camera_unavailable));
            modeValues.add(CastSettings.CaptureMode.FULL_SCREEN);
        }
        bindSpinner(activity, captureMode, modes.toArray(new String[0]), position -> {
            if (position >= 0 && position < modeValues.size()) {
                settings.setCaptureMode(modeValues.get(position));
            }
        });
        int sel = 0;
        for (int i = 0; i < modeValues.size(); i++) {
            if (modeValues.get(i) == settings.getCaptureMode()) {
                sel = i;
                break;
            }
        }
        captureMode.setSelection(sel);
        captureMode.setEnabled(true);
        if (label != null) {
            label.setText(R.string.label_capture_mode);
        }
    }

    private interface IntConsumer {
        void accept(int position);
    }

    private interface LabelProvider<T> {
        String label(AppCompatActivity activity, T value);
    }

    private static <T extends Enum<T>> void bindEnumSpinner(AppCompatActivity activity, Spinner spinner,
            T[] values, LabelProvider<T> labels, int selection, IntConsumer onSelect) {
        String[] items = new String[values.length];
        for (int i = 0; i < values.length; i++) {
            items[i] = labels.label(activity, values[i]);
        }
        bindSpinner(activity, spinner, items, onSelect);
        if (selection >= 0 && selection < values.length) {
            spinner.setSelection(selection);
        }
    }

    private static void bindSpinner(AppCompatActivity activity, Spinner spinner, String[] items,
            IntConsumer onSelect) {
        spinner.setAdapter(new ArrayAdapter<>(activity, android.R.layout.simple_spinner_dropdown_item, items));
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                onSelect.accept(position);
            }

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

    private static void selectCodecSpinner(Spinner spinner, List<CastSettings.VideoCodec> codecs,
            CastSettings.VideoCodec selected) {
        for (int i = 0; i < codecs.size(); i++) {
            if (codecs.get(i) == selected) {
                spinner.setSelection(i);
                return;
            }
        }
    }

    private static String labelVideoCodec(AppCompatActivity activity, CastSettings.VideoCodec codec) {
        switch (codec) {
            case H264:
                return activity.getString(R.string.codec_h264);
            case H265:
                return activity.getString(R.string.codec_h265);
            case PASSTHROUGH:
                return activity.getString(R.string.codec_passthrough);
            case AUTO:
            default:
                return activity.getString(R.string.option_auto);
        }
    }

    private static String labelAdjustment(AppCompatActivity activity, CastSettings.AdjustmentPreset p) {
        switch (p) {
            case NONE:
                return activity.getString(R.string.option_none);
            case QUALITY:
                return activity.getString(R.string.option_quality);
            case SPEED:
                return activity.getString(R.string.option_speed);
            case PERFORMANCE:
                return activity.getString(R.string.option_performance);
            case AUTO:
            default:
                return activity.getString(R.string.option_auto);
        }
    }

    private static String labelBitrateMode(AppCompatActivity activity, CastSettings.BitrateMode m) {
        switch (m) {
            case NONE:
                return activity.getString(R.string.option_none);
            case QUALITY:
                return activity.getString(R.string.option_quality);
            case SPEED:
                return activity.getString(R.string.option_speed);
            case BALANCED:
                return activity.getString(R.string.option_balanced);
            case ROBUST_LESS:
                return activity.getString(R.string.option_robust_less);
            case ESTIMATED:
                return activity.getString(R.string.option_estimated);
            case AUTO:
            default:
                return activity.getString(R.string.option_auto);
        }
    }

    private static String labelNetworkAdoption(AppCompatActivity activity, CastSettings.NetworkAdoption n) {
        switch (n) {
            case QUALITY:
                return activity.getString(R.string.option_quality);
            case SPEED:
                return activity.getString(R.string.option_speed);
            case ESTIMATED:
                return activity.getString(R.string.option_estimated);
            case AUTO:
            default:
                return activity.getString(R.string.option_auto);
        }
    }
}
