package com.nadi.player.nativeplayer.gesture;

import android.app.Activity;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.ProgressBar;

/**
 * Per-window touch gesture handler for the player surface.
 *
 * Two vertical drag zones:
 *   • Left half of the screen  → screen brightness (window-scoped)
 *   • Right half of the screen → media volume (STREAM_MUSIC)
 *
 * Both gestures show a small dim indicator (icon + 0..100% bar) at the
 * top of the screen while the finger is down, then fade out after
 * release. No haptics, no animations — keeping it cheap so it doesn't
 * compete with the live video for CPU on TV boxes / low-end phones.
 *
 * Horizontal swipes are forwarded to the host's own GestureDetector
 * (used for opening / closing the channel panel), so this class only
 * consumes the event when the gesture is decisively vertical.
 */
public final class PlayerGestureHandler {

    /** Minimum vertical travel before we commit to a drag. Below this we
     *  let the parent handle the event (single-tap, horizontal flings). */
    private static final float COMMIT_PX = 24f;

    /** Full screen of vertical travel = 100% change. */
    private static final float FULL_SWEEP_PX = 500f;

    public interface IndicatorView {
        /** Show with {@code mode = 'b'} (brightness) or {@code 'v'} (volume),
         *  current value 0..100. Called many times during a drag. */
        void show(char mode, int valuePercent);
        void hide();
    }

    private final Activity activity;
    private final AudioManager audio;
    private final IndicatorView indicator;
    private final Handler ui = new Handler(Looper.getMainLooper());
    private final Runnable hideRunnable;

    private float startX, startY;
    private float startBrightness;  // 0..1
    private int   startVolume;      // 0..max
    private int   maxVolume;
    private boolean dragging;
    private char   mode = 0; // 'b' / 'v' / 0=undecided
    private boolean leftSide;

    public PlayerGestureHandler(Activity activity, IndicatorView indicator) {
        this.activity = activity;
        this.indicator = indicator;
        this.audio = (AudioManager) activity.getSystemService(Activity.AUDIO_SERVICE);
        this.maxVolume = audio == null ? 15 : audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        this.hideRunnable = () -> {
            if (indicator != null) indicator.hide();
        };
    }

    /** Hook for the host activity's touch listener.
     *  Returns true if the gesture was consumed (vertical drag in progress).
     *  When false, the host should let other detectors handle the event. */
    public boolean onTouch(View v, MotionEvent ev) {
        if (v == null) return false;
        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                startX = ev.getX();
                startY = ev.getY();
                leftSide = startX < v.getWidth() / 2f;
                dragging = false;
                mode = 0;
                startBrightness = currentBrightness();
                startVolume = audio == null ? 0
                        : audio.getStreamVolume(AudioManager.STREAM_MUSIC);
                return false; // let parent see DOWN (tap-to-toggle controls)
            case MotionEvent.ACTION_MOVE: {
                float dx = ev.getX() - startX;
                float dy = ev.getY() - startY;
                if (!dragging) {
                    // Commit to vertical only when |dy| clearly dominates.
                    if (Math.abs(dy) > COMMIT_PX
                            && Math.abs(dy) > Math.abs(dx) * 1.5f) {
                        dragging = true;
                        mode = leftSide ? 'b' : 'v';
                    } else {
                        return false;
                    }
                }
                // Drag UP = increase, DOWN = decrease.
                float pct = -dy / FULL_SWEEP_PX;
                if (mode == 'b') {
                    float next = clamp01(startBrightness + pct);
                    applyBrightness(next);
                    if (indicator != null) indicator.show('b', Math.round(next * 100f));
                } else {
                    int next = (int) Math.round(startVolume + pct * maxVolume);
                    next = Math.max(0, Math.min(maxVolume, next));
                    if (audio != null) {
                        audio.setStreamVolume(AudioManager.STREAM_MUSIC, next, 0);
                    }
                    int percent = maxVolume == 0 ? 0
                            : Math.round(next * 100f / maxVolume);
                    if (indicator != null) indicator.show('v', percent);
                }
                return true;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (dragging) {
                    // Brief lingering display, then fade.
                    ui.removeCallbacks(hideRunnable);
                    ui.postDelayed(hideRunnable, 600L);
                }
                boolean consumed = dragging;
                dragging = false;
                mode = 0;
                return consumed;
            default:
                return false;
        }
    }

    /* ===== helpers ===== */

    private float currentBrightness() {
        if (activity == null) return 0.5f;
        WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
        // BRIGHTNESS_OVERRIDE_NONE (-1) means "use system" — start from system level.
        if (lp.screenBrightness < 0f) {
            try {
                int sys = Settings.System.getInt(
                        activity.getContentResolver(),
                        Settings.System.SCREEN_BRIGHTNESS);
                return clamp01(sys / 255f);
            } catch (Settings.SettingNotFoundException e) {
                return 0.5f;
            }
        }
        return clamp01(lp.screenBrightness);
    }

    private void applyBrightness(float v) {
        if (activity == null) return;
        WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
        lp.screenBrightness = v;
        activity.getWindow().setAttributes(lp);
    }

    private static float clamp01(float v) {
        if (v < 0.01f) return 0.01f;        // avoid pitch-black "stuck" UI
        if (v > 1f) return 1f;
        return v;
    }

    /**
     * Lightweight reference implementation of {@link IndicatorView} that
     * drives a tiny icon + horizontal progress bar at the top of the
     * screen. PlayerActivity creates these once in onCreate.
     */
    public static final class IconBarIndicator implements IndicatorView {
        private final View container;
        private final ImageView icon;
        private final ProgressBar bar;
        private final int iconBrightnessRes;
        private final int iconVolumeRes;

        public IconBarIndicator(View container, ImageView icon, ProgressBar bar,
                                int iconBrightnessRes, int iconVolumeRes) {
            this.container = container;
            this.icon = icon;
            this.bar = bar;
            this.iconBrightnessRes = iconBrightnessRes;
            this.iconVolumeRes = iconVolumeRes;
            container.setVisibility(View.GONE);
            container.setAlpha(0f);
        }

        @Override
        public void show(char mode, int valuePercent) {
            icon.setImageResource(mode == 'b' ? iconBrightnessRes : iconVolumeRes);
            bar.setProgress(valuePercent);
            if (container.getVisibility() != View.VISIBLE) {
                container.setVisibility(View.VISIBLE);
                container.animate().cancel();
                container.animate().alpha(1f).setDuration(120L).start();
            }
        }

        @Override
        public void hide() {
            container.animate().cancel();
            container.animate().alpha(0f).setDuration(160L)
                    .withEndAction(() -> container.setVisibility(View.GONE))
                    .start();
        }
    }
}
