package com.volcengine.velive.rn.pull.pictureInpicture;

import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.provider.Settings;
import android.util.DisplayMetrics; // Import DisplayMetrics
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.volcengine.velive.rn.pull.R;
import java.util.List;

public class FloatingWindowService extends Service {
  private static final int MARGIN_TOP = 40;
  private static final int MARGIN_BOTTOM = 40;
  private static final int MARGIN_LEFT = 20;
  private static final int MARGIN_RIGHT = 20;
  private static final String TAG = FloatingWindowService.class.getSimpleName();

  private WindowManager mWindowManager;
  private WindowManager.LayoutParams mLayoutParams;
  private SurfaceView mSurfaceView;
  private View mSmallWindowView;
  private ActivityLaunchReceiver mActivityLaunchReceiver;
  private FloatingOnTouchListener
      mFloatingOnTouchListener; // Store the listener instance

  public static final String ACTION_STOP_PIP_SERVICE =
      "com.volcengine.velive.rn.pull.STOP_PIP_SERVICE";
  public static final String INTENT_EXTRA_KEY_ASPECT_RATIO = "aspect_ratio";
  public static final String INTENT_EXTRA_KEY_X_POS = "x_pos";
  public static final String INTENT_EXTRA_KEY_Y_POS = "y_pos";

  @Override
  public void onCreate() {
    Log.d(TAG, "onCreate");
    super.onCreate();
    FloatingWindowHelper.getInstance().onStartService();
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "onStartCommand");
    initUI(intent.getFloatExtra(INTENT_EXTRA_KEY_ASPECT_RATIO, 16f / 9f),
           intent.getIntExtra(INTENT_EXTRA_KEY_X_POS, 300),
           intent.getIntExtra(INTENT_EXTRA_KEY_Y_POS, 300));
    FloatingWindowHelper.getInstance().setSurfaceView(mSurfaceView);
    return super.onStartCommand(intent, flags, startId);
  }

  @Override
  public void onDestroy() {
    Log.d(TAG, "onDestroy");
    super.onDestroy();
    
    unregisterActivityLaunchReceiver(); // Ensure receiver is unregistered on
                                        // destroy
    if (mSmallWindowView != null) {
      try {
        mWindowManager.removeView(mSmallWindowView);
      } catch (Exception e) {
        Log.e(TAG, "Error removing view: " + e.getMessage());
      }
    }
    FloatingWindowHelper.getInstance().onStopService();
  }

  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null;
  }

  private void initUI(float aspectRatio, int x, int y) {
    mWindowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
    mLayoutParams = new WindowManager.LayoutParams();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
      mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
      mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
    }
    mLayoutParams.format = PixelFormat.RGBA_8888;
    mLayoutParams.gravity = Gravity.START | Gravity.TOP;
    mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                          WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

    // Get screen dimensions
    DisplayMetrics displayMetrics = new DisplayMetrics();
    mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);
    int screenWidth = displayMetrics.widthPixels;
    int screenHeight = displayMetrics.heightPixels;

    int drawableWidth = screenWidth - MARGIN_LEFT - MARGIN_RIGHT;
    int drawableHeight = screenHeight - MARGIN_TOP - MARGIN_BOTTOM;

    boolean isPortrait = screenHeight > screenWidth;

    int maxLen;
    if (isPortrait) {
      // in portrait mode, width cannot exceed drawableWidth
      maxLen = drawableWidth;
    } else {
      // in landscape mode, height cannot exceed drawableHeight
      maxLen = drawableHeight;
    }

    int width, height;
    if (aspectRatio >= 1) { //  wider than tall (landscape or square video)
      width = maxLen;
      height = (int)(width / aspectRatio);
      if (isPortrait && width > drawableWidth) {
        width = drawableWidth;
        height = (int)(width / aspectRatio);
      }
      if (!isPortrait &&
          height > drawableHeight) { // 横屏下，若计算出的高度超出drawableHeight
        height = drawableHeight;
        width = (int)(height * aspectRatio);
      }
    } else { // 高大于宽 (竖向视频)
      height = maxLen;
      width = (int)(height * aspectRatio);
      if (!isPortrait && height > drawableHeight) {
        height = drawableHeight;
        width = (int)(height * aspectRatio);
      }
      if (isPortrait &&
          width > drawableWidth) { // 竖屏下，若计算出的宽度超出drawableWidth
        width = drawableWidth;
        height = (int)(width / aspectRatio);
      }
    }

    // Ensure dimensions are positive
    mLayoutParams.width = Math.max(1, width);
    mLayoutParams.height = Math.max(1, height);

    // Initial position of the floating window, respecting margins
    mLayoutParams.x =
        Math.max(MARGIN_LEFT,
                 Math.min(x, screenWidth - mLayoutParams.width - MARGIN_RIGHT));
    mLayoutParams.y =
        Math.max(MARGIN_TOP, Math.min(y, screenHeight - mLayoutParams.height -
                                             MARGIN_BOTTOM));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      if (Settings.canDrawOverlays(this)) {
        LayoutInflater layoutInflater = LayoutInflater.from(this);
        mSmallWindowView =
            layoutInflater.inflate(R.layout.floating_window_layout, null);
        this.mFloatingOnTouchListener =
            new FloatingOnTouchListener(); // Create and store the instance
        mSmallWindowView.setOnTouchListener(
            this.mFloatingOnTouchListener); // Set the stored instance
        mWindowManager.addView(mSmallWindowView, mLayoutParams);
        mSurfaceView = mSmallWindowView.findViewById(R.id.surface_view);
        mSmallWindowView.findViewById(R.id.surface_close_btn)
            .setOnClickListener(v -> {
              // 触发关闭按钮点击回调
              FloatingWindowHelper.getInstance().onClickFloatingWindowCloseBtn(FloatingWindowService.this);
              unregisterActivityLaunchReceiver();
              stopSelf();
            });

        mSmallWindowView.findViewById(R.id.new_window_btn)
            .setOnClickListener(v -> {
              Log.d(TAG, "PIP window clicked");

              try {
                if (isAppRunningInForeground()) {
                  unregisterActivityLaunchReceiver();
                  stopSelf();
                  return;
                }

                // Get the main activity
                String packageName = this.getPackageName();
                Intent launchIntent =
                    this.getPackageManager().getLaunchIntentForPackage(packageName);
                if (launchIntent != null) {
                  launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                  // Register receiver to stop service when activity is launched
                  registerActivityLaunchReceiver();
                  this.startActivity(launchIntent);
                  
                  // Wait for app to reach foreground before closing floating window
                  checkAppForegroundAndClose();
                }
              } catch (Exception e) {
                
              }
          });
      }
    }
  }

  private boolean isAppRunningInForeground() {
    ActivityManager activityManager =
        (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
    if (activityManager == null) {
      return false;
    }

    String packageName = getPackageName();

    // For Android API level 21 and above
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      List<ActivityManager.RunningAppProcessInfo> runningProcesses =
          activityManager.getRunningAppProcesses();
      if (runningProcesses != null) {
        for (ActivityManager.RunningAppProcessInfo processInfo :
             runningProcesses) {
          if (processInfo.processName.equals(packageName)) {
            // Check if the process is in foreground
            if (processInfo.importance ==
                ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
              return true;
            }
          }
        }
      }
    } else {
      // For older Android versions, use deprecated method
      @SuppressWarnings("deprecation")
      List<ActivityManager.RunningTaskInfo> runningTasks =
          activityManager.getRunningTasks(1);
      if (runningTasks != null && !runningTasks.isEmpty()) {
        ActivityManager.RunningTaskInfo topTask = runningTasks.get(0);
        if (topTask.topActivity != null &&
            packageName.equals(topTask.topActivity.getPackageName())) {
          return true;
        }
      }
    }
    return false;
  }

  private void registerActivityLaunchReceiver() {
    if (mActivityLaunchReceiver == null) {
      mActivityLaunchReceiver = new ActivityLaunchReceiver();
      IntentFilter filter = new IntentFilter(ACTION_STOP_PIP_SERVICE);
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        registerReceiver(mActivityLaunchReceiver, filter,
                         Context.RECEIVER_EXPORTED);
      } else {
        registerReceiver(mActivityLaunchReceiver, filter);
      }
      Log.d(TAG, "ActivityLaunchReceiver registered");
    }
  }

  private void unregisterActivityLaunchReceiver() {
    if (mActivityLaunchReceiver != null) {
      try {
        unregisterReceiver(mActivityLaunchReceiver);
        mActivityLaunchReceiver = null;
        Log.d(TAG, "ActivityLaunchReceiver unregistered");
      } catch (IllegalArgumentException e) {
        Log.e(TAG, "Receiver not registered: " + e.getMessage());
      }
    }
  }

  private Handler mHandler = new Handler(Looper.getMainLooper());
  
  private void checkAppForegroundAndClose() {
    mHandler.postDelayed(new Runnable() {
      private int checkCount = 0;
      
      @Override
      public void run() {
        if (isAppRunningInForeground()) {
          unregisterActivityLaunchReceiver();
          stopSelf();
        } else if (checkCount < 20) { // Check up to 20 times
          checkCount++;
          mHandler.postDelayed(this, 500);
        }
      }
    }, 500);
  }

  // BroadcastReceiver to listen for activity launch confirmation
  private class ActivityLaunchReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      Log.d(TAG, "Received broadcast to stop PIP service");
      if (ACTION_STOP_PIP_SERVICE.equals(intent.getAction())) {
        stopSelf();                         // Stop the service
        unregisterActivityLaunchReceiver(); // Unregister receiver
      }
    }
  }

  private class FloatingOnTouchListener implements View.OnTouchListener {
    private boolean isDoubleClickAnimating =
        false; // Flag to indicate if double-click animation is running
    private final int touchSlop =
        ViewConfiguration
            .get(FloatingWindowService.this.getApplicationContext())
            .getScaledTouchSlop();
    private int downX, downY;
    private int x, y;
    private boolean isDragging;

    private static final int MODE_NONE = 0;
    private static final int MODE_DRAG = 1;
    private static final int MODE_ZOOM = 2;
    private int mode = MODE_NONE;

    private float oldDist = 1f;
    private float initialAspectRatio;
    private int initialWidth;
    private int initialHeight;

    // Screen metrics
    private DisplayMetrics displayMetrics = new DisplayMetrics();
    private int screenWidth;
    private int screenHeight;
    private int drawableWidth;
    private int drawableHeight;
    private boolean isPortraitScreen;

    private long lastClickTime = 0;
    private static final long DOUBLE_CLICK_TIME_DELTA = 200; // milliseconds

    private final float ZOOM_SCALE_FACTOR =
        1.5f; // Factor to zoom in/out on double tap

    private void updateScreenMetrics() {
      mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);
      screenWidth = displayMetrics.widthPixels;
      screenHeight = displayMetrics.heightPixels;
      drawableWidth = screenWidth - MARGIN_LEFT - MARGIN_RIGHT;
      drawableHeight = screenHeight - MARGIN_TOP - MARGIN_BOTTOM;
      isPortraitScreen = screenHeight > screenWidth;
    }

    @Override
    public boolean onTouch(View view, MotionEvent event) {
      // Update screen metrics at the beginning of a touch gesture,
      // as orientation or screen size might have changed since last touch.
      if (event.getAction() == MotionEvent.ACTION_DOWN) {
        updateScreenMetrics();
      }
      // If currently animating from double click, consume touch events to
      // prevent interference
      if (isDoubleClickAnimating) {
        // Allow ACTION_UP to potentially clear the flag if animation ends early
        // or is cancelled but primarily, prevent new gestures from starting.
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
          mode = MODE_NONE; // Prevent drag/zoom from starting
        }
        return true; // Consume the event
      }
      switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_DOWN:
        downX = x = (int)event.getRawX();
        downY = y = (int)event.getRawY();
        mode = MODE_DRAG;
        initialAspectRatio = (float)mLayoutParams.width / mLayoutParams.height;
        initialWidth = mLayoutParams.width;
        initialHeight = mLayoutParams.height;

        // Double tap detection
        long clickTime = System.currentTimeMillis();
        if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) {
          handleDoubleClick(view);
        }
        lastClickTime = clickTime;
        break;
      case MotionEvent.ACTION_POINTER_DOWN:
        oldDist = spacing(event);
        if (oldDist > 10f) {
          mode = MODE_ZOOM;
        }
        break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_POINTER_UP:
        boolean wasZooming = (mode == MODE_ZOOM);
        // Capture params before mode is reset
        int xBeforeSnap = mLayoutParams.x;
        int widthBeforeSnap = mLayoutParams.width;

        mode = MODE_NONE; // Reset mode

        if (isDragging) {
          isDragging = false;
          // Perform snap animation if it was a drag or end of zoom
          if (event.getPointerCount() == 1 || wasZooming) {
            Log.i(TAG, "ACTION_UP/POINTER_UP: Triggering snap. xBeforeSnap=" +
                           xBeforeSnap + ", widthBeforeSnap=" +
                           widthBeforeSnap + ", wasZooming=" + wasZooming +
                           ", pointerCount=" + event.getPointerCount());
            animateSnapToEdge(view);
          }
        } else {
          // Only trigger click if not zooming and it's a single pointer up
          if (event.getPointerCount() == 1 &&
              (event.getAction() & MotionEvent.ACTION_MASK) ==
                  MotionEvent.ACTION_UP) {
            // Check if it was a drag or a tap
            if (Math.abs(event.getRawX() - downX) < touchSlop &&
                Math.abs(event.getRawY() - downY) < touchSlop) {
              // This is a tap, not a double tap, so handle as single click if
              // needed For now, double tap handles zoom, single tap is handled
              // by new_window_btn
            }
          }
        }
        // downX and downY are reset at ACTION_DOWN for the primary pointer
        break;

      case MotionEvent.ACTION_MOVE:
        if (mode == MODE_DRAG && event.getPointerCount() == 1) {
          int nowX = (int)event.getRawX();
          int nowY = (int)event.getRawY();
          if (!isDragging && (Math.abs(nowX - downX) > touchSlop ||
                              Math.abs(nowY - downY) > touchSlop)) {
            isDragging = true;
          }
          if (isDragging) {
            int movedX = nowX - x;
            int movedY = nowY - y;
            mLayoutParams.x = mLayoutParams.x + movedX;
            mLayoutParams.y = mLayoutParams.y + movedY;

            mLayoutParams.y = Math.max(
                MARGIN_TOP, Math.min(mLayoutParams.y, this.screenHeight -
                                                          mLayoutParams.height -
                                                          MARGIN_BOTTOM));

            mWindowManager.updateViewLayout(view, mLayoutParams);
          }
          x = nowX;
          y = nowY;
        } else if (mode == MODE_ZOOM && event.getPointerCount() >= 2) {
          float newDist = spacing(event);
          if (newDist > 10f) {
            // Calculate midpoint of the two fingers
            float midX = (event.getX(0) + event.getX(1)) / 2;
            float midY = (event.getY(0) + event.getY(1)) / 2;

            // Convert midpoint from view coordinates to screen coordinates
            float screenMidX = mLayoutParams.x + midX;
            float screenMidY = mLayoutParams.y + midY;

            float scale = newDist / oldDist;
            int oldWidth = mLayoutParams.width;
            int oldHeight = mLayoutParams.height;
            int newWidth = (int)(oldWidth * scale);
            int newHeight = (int)(oldHeight * scale);

            // Use pre-calculated screen metrics
            // Screen metrics are now updated in onTouch ACTION_DOWN, so they
            // should be current here.

            // Min size constraints: 1/2 of initial size, but not smaller than a
            // fraction of screen respecting margins
            int minAllowedWidth;
            int minAllowedHeight;
            if (isPortraitScreen) {
              minAllowedWidth = Math.max(initialWidth / 2, drawableWidth / 2);
              minAllowedHeight = (int)(minAllowedWidth / initialAspectRatio);
            } else {
              minAllowedHeight =
                  Math.max(initialHeight / 2, drawableHeight / 2);
              minAllowedWidth = (int)(minAllowedHeight * initialAspectRatio);
            }
            // Ensure min dimensions are at least 1
            minAllowedWidth = Math.max(1, minAllowedWidth);
            minAllowedHeight = Math.max(1, minAllowedHeight);

            // Max size constraints based on orientation and margins
            int maxAllowedWidth, maxAllowedHeight;
            if (isPortraitScreen) {
              maxAllowedWidth = drawableWidth;
              maxAllowedHeight =
                  (int)(maxAllowedWidth /
                        initialAspectRatio); // Maintain aspect ratio
              if (maxAllowedHeight >
                  drawableHeight) { // If calculated height is too much for
                                    // portrait
                maxAllowedHeight = drawableHeight;
                maxAllowedWidth = (int)(maxAllowedHeight * initialAspectRatio);
              }
            } else { // Landscape screen
              maxAllowedHeight = drawableHeight;
              maxAllowedWidth =
                  (int)(maxAllowedHeight *
                        initialAspectRatio);         // Maintain aspect ratio
              if (maxAllowedWidth > drawableWidth) { // If calculated width is
                                                     // too much for landscape
                maxAllowedWidth = drawableWidth;
                maxAllowedHeight = (int)(maxAllowedWidth / initialAspectRatio);
              }
            }
            // maxAllowedWidth = Math.min(maxAllowedWidth, LONGER_SIDE_MAX_LEN);
            // // Global max len - Removed maxAllowedHeight =
            // Math.min(maxAllowedHeight, LONGER_SIDE_MAX_LEN); // Global max
            // len - Removed
            if (initialAspectRatio >= 1)
              maxAllowedHeight = (int)(maxAllowedWidth / initialAspectRatio);
            else
              maxAllowedWidth = (int)(maxAllowedHeight * initialAspectRatio);

            // Apply scaling
            newWidth = (int)(oldWidth * scale);
            newHeight = (int)(oldHeight * scale);

            // Clamp to min/max size while maintaining aspect ratio
            if (scale < 1) { // Shrinking
              newWidth = Math.max(minAllowedWidth, newWidth);
              newHeight = (int)(newWidth / initialAspectRatio);
              if (newHeight < minAllowedHeight) {
                newHeight = minAllowedHeight;
                newWidth = (int)(newHeight * initialAspectRatio);
              }
            } else { // Expanding
              newWidth = Math.min(maxAllowedWidth, newWidth);
              newHeight = (int)(newWidth / initialAspectRatio);
              if (newHeight > maxAllowedHeight) {
                newHeight = maxAllowedHeight;
                newWidth = (int)(newHeight * initialAspectRatio);
              }
            }

            // Final check to ensure dimensions are within absolute min/max
            // bounds
            newWidth =
                Math.max(minAllowedWidth, Math.min(newWidth, maxAllowedWidth));
            newHeight =
                (int)(newWidth / initialAspectRatio); // Maintain aspect ratio
            // Ensure height is also clamped correctly after width adjustment
            if (newHeight < minAllowedHeight) {
              newHeight = minAllowedHeight;
              newWidth = (int)(newHeight * initialAspectRatio);
            }
            if (newHeight > maxAllowedHeight) {
              newHeight = maxAllowedHeight;
              newWidth = (int)(newHeight * initialAspectRatio);
            }
            // Re-clamp width just in case height clamping affected it and
            // pushed it out of width bounds
            newWidth =
                Math.max(minAllowedWidth, Math.min(newWidth, maxAllowedWidth));

            // Adjust position to keep the zoom centered on the midpoint
            mLayoutParams.x = (int)(screenMidX - (midX * newWidth / oldWidth));
            mLayoutParams.y =
                (int)(screenMidY - (midY * newHeight / oldHeight));

            mLayoutParams.width = newWidth;
            mLayoutParams.height = newHeight;

            // Prevent window from going off-screen after repositioning
            // For Y, keep margin constraints
            mLayoutParams.y = Math.max(
                MARGIN_TOP,
                Math.min(mLayoutParams.y,
                         screenHeight - mLayoutParams.height - MARGIN_BOTTOM));
            // For X, constrain to screen physical edges only during zoom,
            // let animateSnapToEdge handle final margin snapping.
            mLayoutParams.x = Math.max(
                0, // Screen physical left edge
                Math.min(
                    mLayoutParams.x,
                    screenWidth -
                        mLayoutParams.width)); // Screen physical right edge

            // 更新SurfaceView的布局参数
            if (mSurfaceView != null) {
              android.view.ViewGroup.LayoutParams surfaceParams =
                  mSurfaceView.getLayoutParams();
              if (surfaceParams != null) {
                surfaceParams.width = mLayoutParams.width;
                surfaceParams.height = mLayoutParams.height;
                mSurfaceView.setLayoutParams(surfaceParams);
              }
            }
            mWindowManager.updateViewLayout(view, mLayoutParams);
            oldDist = newDist;
          }
          isDragging =
              true; // Zooming implies dragging state for click handling
        }
        break;

      default:
        break;
      }
      return true;
    }

    private float spacing(MotionEvent event) {
      if (event.getPointerCount() < 2)
        return 0f;
      float x = event.getX(0) - event.getX(1);
      float y = event.getY(0) - event.getY(1);
      return (float)Math.sqrt(x * x + y * y);
    }

    private void handleDoubleClick(View view) {
      Log.d(TAG, "Double tap detected");
      int targetWidth, targetHeight;

      // Use pre-calculated screen metrics
      // updateScreenMetrics(); // Ensure metrics are fresh if not updated in
      // onTouch ACTION_DOWN Screen metrics are now updated in onTouch
      // ACTION_DOWN, so they should be current here.

      // Minimum size: 1/2 of initial size, but not smaller than a fraction of
      // screen respecting margins
      int minAllowedWidth = initialWidth / 2;
      int minAllowedHeight = initialHeight / 2;
      if (isPortraitScreen) {
        minAllowedWidth = Math.max(minAllowedWidth, drawableWidth / 2);
        minAllowedHeight = (int)(minAllowedWidth / initialAspectRatio);
      } else {
        minAllowedHeight = Math.max(minAllowedHeight, drawableHeight / 2);
        minAllowedWidth = (int)(minAllowedHeight * initialAspectRatio);
      }

      // Maximum size based on orientation and margins
      int maxAllowedTargetWidth, maxAllowedTargetHeight;
      if (isPortraitScreen) {
        maxAllowedTargetWidth = drawableWidth;
        maxAllowedTargetHeight =
            (int)(maxAllowedTargetWidth / initialAspectRatio);
        if (maxAllowedTargetHeight > drawableHeight) {
          maxAllowedTargetHeight = drawableHeight;
          maxAllowedTargetWidth =
              (int)(maxAllowedTargetHeight * initialAspectRatio);
        }
      } else { // Landscape screen
        maxAllowedTargetHeight = drawableHeight;
        maxAllowedTargetWidth =
            (int)(maxAllowedTargetHeight * initialAspectRatio);
        if (maxAllowedTargetWidth > drawableWidth) {
          maxAllowedTargetWidth = drawableWidth;
          maxAllowedTargetHeight =
              (int)(maxAllowedTargetWidth / initialAspectRatio);
        }
      }
      // Apply global LONGER_SIDE_MAX_LEN constraint - Removed
      // if (initialAspectRatio >= 1) { // Landscape or square video
      //   maxAllowedTargetWidth =
      //       Math.min(maxAllowedTargetWidth, LONGER_SIDE_MAX_LEN);
      //   maxAllowedTargetHeight =
      //       (int)(maxAllowedTargetWidth / initialAspectRatio);
      // } else { // Portrait video
      //   maxAllowedTargetHeight =
      //       Math.min(maxAllowedTargetHeight, LONGER_SIDE_MAX_LEN);
      //   maxAllowedTargetWidth =
      //       (int)(maxAllowedTargetHeight * initialAspectRatio);
      // }

      int currentX = mLayoutParams.x;
      int currentY = mLayoutParams.y;
      int currentWidth = mLayoutParams.width;
      int currentHeight = mLayoutParams.height;

      int finalTargetX = currentX;
      int finalTargetY = currentY;

      // 计算当前宽度与中间值的比较，决定缩放方向
      int midWidth = (minAllowedWidth + maxAllowedTargetWidth) / 2;
      if (currentWidth < midWidth) {
        // 当前更接近最小值，放大到最大尺寸
        targetWidth = maxAllowedTargetWidth;
        targetHeight = maxAllowedTargetHeight;
        Log.d(TAG, "Zooming in to max: " + targetWidth + "x" + targetHeight);
      } else {
        // 当前更接近最大值，缩小到最小尺寸
        targetWidth = minAllowedWidth;
        targetHeight = minAllowedHeight;
        Log.d(TAG, "Zooming out to min: " + targetWidth + "x" + targetHeight);
      }

      // Ensure target dimensions respect calculated min/max and screen
      // boundaries with margins
      targetWidth = Math.max(minAllowedWidth,
                             Math.min(targetWidth, maxAllowedTargetWidth));
      targetHeight = (int)(targetWidth / initialAspectRatio);
      if (targetHeight < minAllowedHeight) {
        targetHeight = minAllowedHeight;
        targetWidth = (int)(targetHeight * initialAspectRatio);
      }
      if (targetHeight > maxAllowedTargetHeight) {
        targetHeight = maxAllowedTargetHeight;
        targetWidth = (int)(targetHeight * initialAspectRatio);
      }
      // Final check for width after height adjustment
      targetWidth = Math.max(minAllowedWidth,
                             Math.min(targetWidth, maxAllowedTargetWidth));

      // Instantly apply new size and update layout BEFORE starting
      // position/size animations This helps in reducing black bars by setting
      // the target container size first.
      mLayoutParams.width = targetWidth;
      mLayoutParams.height = targetHeight;
      if (mSmallWindowView != null &&
          mSmallWindowView.getWindowToken() != null) {
        mWindowManager.updateViewLayout(mSmallWindowView, mLayoutParams);
        // Also update SurfaceView's layout params to match the new window size
        // immediately
        if (mSurfaceView != null) {
          android.view.ViewGroup.LayoutParams surfaceParams =
              mSurfaceView.getLayoutParams();
          if (surfaceParams != null) {
            surfaceParams.width = targetWidth;
            surfaceParams.height = targetHeight;
            mSurfaceView.setLayoutParams(surfaceParams);
            mSurfaceView
                .requestLayout(); // Crucial for SurfaceView to redraw correctly
          }
        }
      }

      // Determine window corner for zoom anchor, considering margins
      boolean isTop =
          currentY < (screenHeight - MARGIN_TOP - MARGIN_BOTTOM) / 2 -
                         currentHeight / 2 + MARGIN_TOP;
      boolean isLeft =
          currentX < (screenWidth - MARGIN_LEFT - MARGIN_RIGHT) / 2 -
                         currentWidth / 2 + MARGIN_LEFT;

      if (isLeft && isTop) { // Top-left
        finalTargetX = MARGIN_LEFT;
        finalTargetY = MARGIN_TOP;
      } else if (!isLeft && isTop) { // Top-right
        finalTargetX = screenWidth - targetWidth - MARGIN_RIGHT;
        finalTargetY = MARGIN_TOP;
      } else if (isLeft && !isTop) { // Bottom-left
        finalTargetX = MARGIN_LEFT;
        finalTargetY = screenHeight - targetHeight - MARGIN_BOTTOM;
      } else { // Bottom-right
        finalTargetX = screenWidth - targetWidth - MARGIN_RIGHT;
        finalTargetY = screenHeight - targetHeight - MARGIN_BOTTOM;
      }

      // Ensure target position is within drawable area
      finalTargetX = Math.max(
          MARGIN_LEFT,
          Math.min(finalTargetX, screenWidth - targetWidth - MARGIN_RIGHT));
      finalTargetY = Math.max(
          MARGIN_TOP,
          Math.min(finalTargetY, screenHeight - targetHeight - MARGIN_BOTTOM));

      final int finalTargetWidth = targetWidth;
      final int finalTargetHeight = targetHeight;

      // Make copies for use in animator listeners
      final int animFinalTargetX = finalTargetX;
      final int animFinalTargetY = finalTargetY;

      ValueAnimator xAnimator = ValueAnimator.ofInt(currentX, finalTargetX);
      xAnimator.setDuration(200);
      xAnimator.addUpdateListener(animation -> {
        mLayoutParams.x = (Integer)animation.getAnimatedValue();
        mWindowManager.updateViewLayout(view, mLayoutParams);
      });

      ValueAnimator yAnimator = ValueAnimator.ofInt(currentY, finalTargetY);
      yAnimator.setDuration(200);
      yAnimator.addUpdateListener(animation -> {
        mLayoutParams.y = (Integer)animation.getAnimatedValue();
        mWindowManager.updateViewLayout(view, mLayoutParams);
      });

      ValueAnimator widthAnimator =
          ValueAnimator.ofInt(currentWidth, finalTargetWidth);
      widthAnimator.setDuration(200);
      widthAnimator.addUpdateListener(animation -> {
        mLayoutParams.width = (Integer)animation.getAnimatedValue();
        if (mSurfaceView != null) { // Ensure mSurfaceView is not null
          android.view.ViewGroup.LayoutParams surfaceParams =
              mSurfaceView.getLayoutParams();
          if (surfaceParams != null) { // Ensure surfaceParams is not null
            surfaceParams.width = mLayoutParams.width;
            mSurfaceView.setLayoutParams(surfaceParams);
          }
        }
        mWindowManager.updateViewLayout(view, mLayoutParams);
      });

      ValueAnimator heightAnimator =
          ValueAnimator.ofInt(currentHeight, finalTargetHeight);
      heightAnimator.setDuration(200);
      heightAnimator.addUpdateListener(animation -> {
        mLayoutParams.height = (Integer)animation.getAnimatedValue();
        if (mSurfaceView != null) { // Ensure mSurfaceView is not null
          android.view.ViewGroup.LayoutParams surfaceParams =
              mSurfaceView.getLayoutParams();
          if (surfaceParams != null) { // Ensure surfaceParams is not null
            surfaceParams.height = mLayoutParams.height;
            mSurfaceView.setLayoutParams(surfaceParams);
          }
        }
        mWindowManager.updateViewLayout(view, mLayoutParams);
      });

      android.animation.AnimatorSet animatorSet =
          new android.animation.AnimatorSet();
      animatorSet.playTogether(xAnimator, yAnimator, widthAnimator,
                               heightAnimator);
      // The isDoubleClickAnimating flag is now managed by
      // FloatingOnTouchListener but we still need to set it within
      // handleDoubleClick's AnimatorSet listeners to ensure it's active for the
      // duration of this specific animation.
      animatorSet.addListener(new android.animation.AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(android.animation.Animator animation) {
          if (FloatingWindowService.this.mFloatingOnTouchListener != null) {
            FloatingWindowService.this.mFloatingOnTouchListener
                .isDoubleClickAnimating = true;
          }
          Log.d(TAG, "Double tap AnimatorSet started.");
        }

        @Override
        public void onAnimationEnd(android.animation.Animator animation) {
          if (FloatingWindowService.this.mFloatingOnTouchListener != null) {
            FloatingWindowService.this.mFloatingOnTouchListener
                .isDoubleClickAnimating = false;
          }
          // ValueAnimators update mLayoutParams continuously via their
          // AnimatorUpdateListener. For safety, explicitly set final position
          // values as the last update might not be exact.
          mLayoutParams.x = animFinalTargetX;
          mLayoutParams.y = animFinalTargetY;
          // mLayoutParams.width and mLayoutParams.height should be at
          // finalTargetWidth/Height due to widthAnimator and heightAnimator's
          // updates.

          // Perform a final update of the window layout with all parameters.
          if (mSmallWindowView != null &&
              mSmallWindowView.getWindowToken() != null) {
            mWindowManager.updateViewLayout(mSmallWindowView, mLayoutParams);
          }

          // Resize SurfaceView (logic moved from postDelayed and refined)
          if (mSurfaceView != null && mLayoutParams != null) {
            android.view.ViewGroup.LayoutParams surfaceParams =
                mSurfaceView.getLayoutParams();
            if (surfaceParams != null) {
              boolean changed = false;
              if (surfaceParams.width != mLayoutParams.width) {
                surfaceParams.width =
                    mLayoutParams.width; // mLayoutParams.width is at its final
                                         // animated value
                changed = true;
              }
              if (surfaceParams.height != mLayoutParams.height) {
                surfaceParams.height =
                    mLayoutParams.height; // mLayoutParams.height is at its
                                          // final animated value
                changed = true;
              }
              if (changed) {
                mSurfaceView.setLayoutParams(surfaceParams);
              }
              mSurfaceView
                  .requestLayout(); // Ensure the SurfaceView redraws correctly
              Log.d(TAG,
                    "SurfaceView dimensions finalized in onAnimationEnd to: " +
                        mLayoutParams.width + "x" + mLayoutParams.height);
            }
          }
          Log.d(TAG, "Double tap AnimatorSet ended. Final state: x=" +
                         mLayoutParams.x + ", y=" + mLayoutParams.y + ", w=" +
                         mLayoutParams.width + ", h=" + mLayoutParams.height);
        }

        @Override
        public void onAnimationCancel(android.animation.Animator animation) {
          if (FloatingWindowService.this.mFloatingOnTouchListener != null) {
            FloatingWindowService.this.mFloatingOnTouchListener
                .isDoubleClickAnimating = false;
          }
          // Animation was cancelled, so explicitly set all mLayoutParams to
          // their final target values.
          mLayoutParams.x = animFinalTargetX;
          mLayoutParams.y = animFinalTargetY;
          mLayoutParams.width = finalTargetWidth;
          mLayoutParams.height = finalTargetHeight;

          if (mSmallWindowView != null &&
              mSmallWindowView.getWindowToken() != null) {
            mWindowManager.updateViewLayout(mSmallWindowView, mLayoutParams);
          }

          // Resize SurfaceView to final target dimensions
          if (mSurfaceView != null && mLayoutParams != null) {
            android.view.ViewGroup.LayoutParams surfaceParams =
                mSurfaceView.getLayoutParams();
            if (surfaceParams != null) {
              if (surfaceParams.width != mLayoutParams.width ||
                  surfaceParams.height != mLayoutParams.height) {
                surfaceParams.width =
                    mLayoutParams.width; // Now finalTargetWidth
                surfaceParams.height =
                    mLayoutParams.height; // Now finalTargetHeight
                mSurfaceView.setLayoutParams(surfaceParams);
              }
              mSurfaceView
                  .requestLayout(); // Ensure the SurfaceView redraws correctly
            }
          }
          Log.d(TAG,
                "Double tap AnimatorSet cancelled. State set to targets: x=" +
                    mLayoutParams.x + ", y=" + mLayoutParams.y + ", w=" +
                    mLayoutParams.width + ", h=" + mLayoutParams.height);
        }
      });
      animatorSet.start();
    }

    private void animateSnapToEdge(View view) {
      // Use pre-calculated screen metrics
      // updateScreenMetrics(); // Ensure metrics are fresh if not updated in
      // onTouch ACTION_DOWN Screen metrics are now updated in onTouch
      // ACTION_DOWN, so they should be current here.
      final int screenWidth = this.screenWidth; // Make final for use in lambda
      final int windowWidthAtAnimationStart =
          mLayoutParams.width; // Capture width at start

      int currentX =
          mLayoutParams
              .x; // This is the x at the moment animateSnapToEdge is called
      int targetX;

      // Determine targetX based on current position and captured width
      if (currentX + windowWidthAtAnimationStart / 2 < screenWidth / 2) {
        targetX = MARGIN_LEFT;
      } else {
        targetX = screenWidth - windowWidthAtAnimationStart - MARGIN_RIGHT;
      }

      Log.i(TAG, "animateSnapToEdge: currentX=" + currentX +
                     ", windowWidthAtAnimStart=" + windowWidthAtAnimationStart +
                     ", screenWidth=" + screenWidth + ", calculatedTargetX=" +
                     targetX + ", currentY=" + mLayoutParams.y +
                     ", currentHeight=" + mLayoutParams.height);

      if (currentX == targetX) {
        Log.i(TAG, "animateSnapToEdge: Already at targetX (" + targetX +
                       "). No animation needed.");
        return;
      }

      ValueAnimator snapAnimator = ValueAnimator.ofInt(currentX, targetX);
      snapAnimator.setDuration(200);
      snapAnimator.addUpdateListener(animation -> {
        mLayoutParams.x = (Integer)animation.getAnimatedValue();
        // Boundary checks using width captured at animation start
        if (mLayoutParams.x < 0) { // Physical screen boundary
          mLayoutParams.x = 0;
        }
        // Ensure the right edge does not go beyond screen width
        if (mLayoutParams.x + windowWidthAtAnimationStart >
            screenWidth) { // Physical screen boundary
          mLayoutParams.x = screenWidth - windowWidthAtAnimationStart;
        }
        try {
          if (mSmallWindowView != null &&
              mSmallWindowView.getWindowToken() !=
                  null) { // Check if view is still attached
            mWindowManager.updateViewLayout(view, mLayoutParams);
          }
        } catch (IllegalArgumentException e) {
          Log.w(TAG, "animateSnapToEdge: View not attached? " + e.getMessage());
          // Cancel animator if view is gone to prevent further errors
          if (animation.isRunning()) {
            animation.cancel();
          }
        }
      });
      snapAnimator.start();
    }
  }
}
