package com.grigowashere.aismap;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.util.Printer;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.view.ViewGroup;
import android.graphics.Color;
import android.view.WindowManager;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import com.google.android.material.bottomsheet.BottomSheetDialog;

import com.grigowashere.aismap.controllers.AppCoordinator;
import com.grigowashere.aismap.controllers.MapController;
import com.grigowashere.aismap.controllers.VesselPathController;
import com.grigowashere.aismap.controllers.CompassController;
import com.grigowashere.aismap.maps.MapInterface;
import com.grigowashere.aismap.maps.MapLibreMapImpl;
import com.grigowashere.aismap.models.Vessel;
import com.grigowashere.aismap.models.AISVessel;
import com.grigowashere.aismap.sensors.CompassSensor;
import com.grigowashere.aismap.view.CompassView;
import com.grigowashere.aismap.view.CoordinatesDockWidget;
import com.grigowashere.aismap.view.BaseDockWidget;
import com.grigowashere.aismap.ui.MenuBinder;
import com.grigowashere.aismap.ui.BottomSheetsBinder;
import com.grigowashere.aismap.ui.PermissionsBinder;
import com.grigowashere.aismap.utils.SettingsManager;
import com.grigowashere.aismap.utils.LogSender;
import com.grigowashere.aismap.utils.MIDToCountry;
import com.grigowashere.aismap.ui.UIRenderingCoordinator;
import com.grigowashere.aismap.ui.BottomSheetsManager;
import com.grigowashere.aismap.ui.UIDataChangeNotifier;
// import com.yandex.mapkit.mapview.MapView;
import org.maplibre.android.maps.MapView;
import org.maplibre.android.MapLibre;
import java.util.List;
import java.util.ArrayList;

import com.grigowashere.aismap.controllers.ControllersFactory;
import com.grigowashere.aismap.controllers.DefaultControllersFactory;

public class MainActivity extends AppCompatActivity {
    
    private static final String TAG = "MainActivity";
    private static final int PERMISSION_REQUEST_CODE = 1001;
    private static final int SETTINGS_REQUEST_CODE = 1002;
    private static final int NOTIFICATION_PERMISSION_REQUEST_CODE = 1003;
    
    // Статическая переменная для отслеживания инициализации Яндекс.Карт
    private static boolean isYandexMapsInitialized = false;
    
    // Флаг для отслеживания первого запуска приложения
    private boolean isFirstStart = true;
    
    private AppCoordinator appCoordinator;
    // UI binders
    private MenuBinder menuBinder;
    private BottomSheetsBinder bottomSheetsBinder;
    private PermissionsBinder permissionsBinder;
    private MapController mapController;
    private CompassController compassController;
    private UIRenderingCoordinator uiCoordinator;
    private MapView mapView;
    private SettingsManager settingsManager;
    
    private ImageButton btnCenterOnVessel;
    private ImageButton btnMapOrientation;
    private ImageButton btnCursorToggle;
    private ImageButton btnSettings;
    private ImageButton btnAisTargets;
    private ImageButton btnGpsSource;
    private LinearLayout controlPanel;
    private CompassView compassView;
    private CoordinatesDockWidget coordinatesWidget;
    
    // Троттлинг для UI обновлений
    private android.os.Handler uiThrottleHandler;
    private Runnable compassUpdateRunnable;
    private Runnable coordinatesUpdateRunnable;
    private Runnable compassButtonRotationRunnable;
    private static final long COMPASS_ANIM_DURATION_MS = 150;
    private Vessel lastCompassVessel;
    private Vessel lastCoordinatesVessel;
    private static final long UI_UPDATE_THROTTLE_MS = 200; // 5 FPS максимум
    private TextView tvGpsAge;
    private TextView tvAisAge;
    private TextView tvBleRssi;
    private TextView tvBleBatt;
    private TextView tvFps;
    private int frameCount = 0;
    private long lastFpsTs = 0L;
    private final android.view.Choreographer.FrameCallback fpsCallback = new android.view.Choreographer.FrameCallback() {
        @Override public void doFrame(long frameTimeNanos) {
            // UI heartbeat: если кадры идут, UI точно жив.
            updateUIActivity();
            frameCount++;
            long now = System.currentTimeMillis();
            if (lastFpsTs == 0L) lastFpsTs = now;
            if (now - lastFpsTs >= 1000) {
                final int fps = frameCount;
                frameCount = 0;
                lastFpsTs = now;
                runOnUiThread(() -> {
                    if (tvFps != null) {
                        tvFps.setText("FPS: " + fps);
                        int color;
                        if (fps >= 55) color = android.graphics.Color.parseColor("#4CAF50");
                        else if (fps >= 40) color = android.graphics.Color.parseColor("#FFC107");
                        else color = android.graphics.Color.parseColor("#F44336");
                        tvFps.setTextColor(color);
                    }
                });
            }
            android.view.Choreographer.getInstance().postFrameCallback(this);
        }
    };
    private android.os.Handler messageAgeHandler;
    private Runnable messageAgeRunnable;
    private BottomSheetsManager bottomSheetsManager;
    
    
    // BottomSheet для отображения информации о нашем судне
    // moved to BottomSheetsManager
    
    // BottomSheet для отображения информации об AIS судне
    // moved to BottomSheetsManager

    // Отложенное центрирование из внешнего интента
    private Double pendingCenterLat = null;
    private Double pendingCenterLon = null;
    
    // Управление экраном
    private boolean keepScreenOn = true;
    
    // UI Watchdog для отслеживания зависаний
    private android.os.Handler uiWatchdogHandler;
    private Runnable uiWatchdogRunnable;
    private long lastUIUpdateTime = 0;
    private static final long UI_WATCHDOG_INTERVAL = 1000; // 1 секунда - быстрая диагностика
    private static final long UI_TIMEOUT = 3000; // 3 секунды без обновлений = зависание
    private final java.util.concurrent.ScheduledExecutorService uiWatchdogScheduler =
            java.util.concurrent.Executors.newSingleThreadScheduledExecutor();
    private volatile long lastUiPongUptimeMs = 0L;
    private volatile long lastUiHangLogUptimeMs = 0L;
    
    // Диагностика компаса
    private long lastCompassLogTime = 0;
    private long lastTouchLogTime = 0;
    private long lastKeyLogTime = 0;
    
    // Throttling для updateControlPanelPosition
    private android.os.Handler controlPanelUpdateHandler;
    private Runnable controlPanelUpdateRunnable;
    private boolean controlPanelUpdatePending = false;
    private static final long CONTROL_PANEL_UPDATE_DELAY = 200; // 200ms throttling
    private int controlPanelUpdateCount = 0; // Для диагностики
    private long lastControlPanelUpdateTime = 0;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Отключено: агрессивная диагностика StrictMode/Looper
        
        // Инициализация MapLibre перед созданием MapView
        try {
            MapLibre.getInstance(this);
        } catch (Exception e) {
            Log.e(TAG, "Ошибка инициализации MapLibre: " + e.getMessage(), e);
        }

        // Edge-to-edge: приложение само раскладывает UI под статус/нав-барами
        // и вырезами камеры. Без этого WindowInsets будут давать нули
        // и координатная панель уедет под системную навигационную кнопку.
        try {
            WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
            getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
            getWindow().setNavigationBarColor(android.graphics.Color.TRANSPARENT);
        } catch (Exception e) {
            Log.w(TAG, "Не удалось включить edge-to-edge: " + e.getMessage());
        }

        setContentView(R.layout.activity_main);
        
        initializeViews();
        initializeControllers();
        setupScreenManagement();
        setupUIWatchdog();
        // checkPermissions() будет вызван в onStart
    }

    // Отключено: принудительное bringToFront панели

    // Отключено: дополнительное логирование событий ввода
    
    private void initializeViews() {
        mapView = findViewById(R.id.map_view);
        btnCenterOnVessel = findViewById(R.id.btn_center_vessel);
        btnMapOrientation = findViewById(R.id.btn_map_orientation);
        btnCursorToggle = findViewById(R.id.btn_cursor_toggle);
        btnSettings = findViewById(R.id.btn_settings);
        btnAisTargets = findViewById(R.id.btn_ais_targets);
        btnGpsSource = findViewById(R.id.btn_gps_source);
        controlPanel = findViewById(R.id.control_panel);
        compassView = findViewById(R.id.compass_view);
        coordinatesWidget = findViewById(R.id.coordinates_widget);
        installMainUiInsets();
        
        // Инициализируем троттлинг
        uiThrottleHandler = new android.os.Handler(android.os.Looper.getMainLooper());
        compassUpdateRunnable = () -> {
            if (compassView != null && lastCompassVessel != null) {
                compassView.setOurVessel(lastCompassVessel);
            }
        };
        coordinatesUpdateRunnable = () -> {
            if (coordinatesWidget != null && lastCoordinatesVessel != null) {
                coordinatesWidget.updateVessel(lastCoordinatesVessel);
            }
        };
        // Периодическое обновление поворота кнопок (компас и ownship)
        compassButtonRotationRunnable = () -> {
            try {
                MapInterface mapIf = mapController != null ? mapController.getCurrentMapInterface() : null;
                if (mapIf != null) {
                    float mapBearing = 0f;
                    try {
                        mapBearing = mapIf.getBearing();
                    } catch (Exception ignore) {}

                    // Кнопка компаса должна указывать на север (противоположно bearing карты)
                    float compassTarget = -mapBearing;
                    applySmoothRotation(btnMapOrientation, compassTarget);

                    // Кнопка корабля указывает курс относительно карты: course - mapBearing
                    if (btnCenterOnVessel != null && appCoordinator != null) {
                        Vessel own = appCoordinator.getOwnVessel();
                        if (own != null) {
                            double course = own.getCourse();
                            if (!Double.isNaN(course)) {
                                float shipTarget = (float)(course - mapBearing);
                                applySmoothRotation(btnCenterOnVessel, shipTarget);
                            }
                        }
                    }

                    // В режимах «по компасу» / «по курсу» непрерывно подстраиваем bearing
                    // карты; в «вручную» не трогаем — пользователь крутит жестом.
                    applyAutoMapBearingIfNeeded(mapIf);
                }
            } catch (Exception ignore) {}
            // Планируем следующее обновление
            if (uiThrottleHandler != null) {
                uiThrottleHandler.postDelayed(compassButtonRotationRunnable, Math.max(100, UI_UPDATE_THROTTLE_MS));
            }
        };
        tvGpsAge = findViewById(R.id.tv_gps_age);
        tvAisAge = findViewById(R.id.tv_ais_age);
        tvBleRssi = findViewById(R.id.tv_ble_rssi);
        tvBleBatt = findViewById(R.id.tv_ble_batt);
        tvFps = findViewById(R.id.tv_fps);
        
        // Инициализируем магнитный компас через CompassController
        // compassSensor = new CompassSensor(this); // Удалено - теперь используется CompassController
        
        // Инициализируем throttling для updateControlPanelPosition
        setupControlPanelThrottling();
        
        initializeBottomSheet();
        setupButtonListeners();
        setupCompass();
        setupCoordinatesWidget();
        setupMessageAgesUpdater();
    }
    
    private void setupButtonListeners() {
        if (btnCenterOnVessel != null) btnCenterOnVessel.setOnClickListener(v -> centerOnVessel());
        if (btnMapOrientation != null) {
            btnMapOrientation.setOnClickListener(v -> cycleMapRotationMode());
        }
        if (btnCursorToggle != null) btnCursorToggle.setOnClickListener(v -> toggleCursor());
        if (btnCursorToggle != null) btnCursorToggle.setOnLongClickListener(v -> { toggleCursor(); return true; });
        if (btnSettings != null) btnSettings.setOnClickListener(v -> showSettings());
        if (btnAisTargets != null) btnAisTargets.setOnClickListener(v -> openAisTargets());
        if (btnGpsSource != null) {
            refreshGpsSourceButtonIcon();
            btnGpsSource.setOnClickListener(v -> toggleGpsSource());
        }
        
        // Кнопка для показа информации о судне
//        Button btnShowVesselInfo = findViewById(R.id.btn_show_vessel_info);
//        if (btnShowVesselInfo != null) {
//            btnShowVesselInfo.setOnClickListener(v -> showOwnVesselBottomSheet());
//        }
    }
    
    private void setupCompass() {
        // Устанавливаем начальный азимут (например, север)
        compassView.setAzimuth(0);
        
        // Устанавливаем компас в dock-режим вверху экрана
        compassView.post(() -> {
            compassView.setDocked(true, true, 0, 0);
            compassView.invalidate(); // Принудительная отрисовка
            // Выровнять паддинги под статус-бар/вырез камеры сразу после
            // первого dock-позиционирования (до этого сторона неизвестна).
            reapplyInsetsToDocks();
        });
        
        // Настраиваем слушатель изменения размера док-виджета
        compassView.setOnDockResizeListener(newHeight -> {
            Log.d(TAG, "Compass dock height changed to: " + newHeight);
            // Обновляем позицию панели управления при любом изменении размера docked виджета
            updateControlPanelPosition();
        });
        
        // Настраиваем слушатель изменения состояния docked
        compassView.setOnDockStateChangeListener((isDocked, isTop) -> {
            Log.d(TAG, "Compass dock state changed: docked=" + isDocked + ", top=" + isTop);
            
            // Перепозиционируем все docked виджеты
            BaseDockWidget.repositionAllDockedWidgets((ViewGroup) compassView.getParent());
            
            updateControlPanelPosition();
            // Док мог поменять сторону — паддинги под системные бары
            // тоже должны переключиться (top <-> bottom).
            reapplyInsetsToDocks();
        });
        //smt changed
        // Настраиваем магнитный компас через CompassController
        if (compassController != null && compassController.isCompassAvailable()) {
            compassController.setListener(new CompassController.CompassControllerListener() {
                @Override
                public void onCompassChanged(float azimuth) {
                    // Диагностика: логируем каждые 10 секунд
                    long now = System.currentTimeMillis();
                    if (now - lastCompassLogTime > 10000) {
                        Log.d(TAG, "🧭 MainActivity: onCompassChanged получен, azimuth=" + azimuth);
                        lastCompassLogTime = now;
                    }
                    
                    // Обновляем компас в UI потоке
                    runOnUiThread(() -> {
                        // Диагностика: проверяем выполнение в UI потоке
                        if (now - lastCompassLogTime > 10000) {
                            Log.d(TAG, "🧭 MainActivity: runOnUiThread выполняется для компаса");
                        }
                        
                        compassView.setAzimuth(azimuth);
                        compassView.setMagneticCompass(azimuth);
                        
                        // Обновляем магнитный компас в модели нашего судна
                        if (appCoordinator != null) {
                            Vessel ourVessel = appCoordinator.getOwnVessel();
                            if (ourVessel != null) {
                                ourVessel.setMagneticCompass(azimuth);
                            }
                        }
                    });
                }
                
                @Override
                public void onCompassError(String error) {
                    Log.e(TAG, "Ошибка компаса: " + error);
                }
            });
            Log.d(TAG, "Magnetic compass controller configured");
        } else {
            Log.w(TAG, "Magnetic compass not available");
        }
        
        // Принудительная отрисовка
        compassView.invalidate();
        
        // Инициализируем начальную позицию панели управления
        compassView.post(() -> {
            updateControlPanelPosition();
        });
        // Стартуем цикл обновления поворотов кнопок
        startCompassButtonsLoop();
    }

    private void startCompassButtonsLoop() {
        if (uiThrottleHandler != null && compassButtonRotationRunnable != null) {
            uiThrottleHandler.removeCallbacks(compassButtonRotationRunnable);
            uiThrottleHandler.post(compassButtonRotationRunnable);
        }
    }

    private void applySmoothRotation(ImageButton button, float targetRotation) {
        if (button == null) return;
        try {
            float current = button.getRotation();
            float normalizedTarget = targetRotation % 360f;
            if (normalizedTarget < -180f) normalizedTarget += 360f;
            if (normalizedTarget > 180f) normalizedTarget -= 360f;
            float delta = normalizedTarget - current;
            if (delta > 180f) delta -= 360f;
            if (delta < -180f) delta += 360f;
            float finalTarget = current + delta;
            button.animate().rotation(finalTarget).setDuration(COMPASS_ANIM_DURATION_MS).setInterpolator(new android.view.animation.DecelerateInterpolator()).start();
        } catch (Exception ignore) {}
    }
    
    private void setupCoordinatesWidget() {
        // Настраиваем слушатель изменения размера dock-виджета
        coordinatesWidget.setOnDockResizeListener(newHeight -> {
            Log.d(TAG, "Coordinates dock height changed to: " + newHeight);
            // Обновляем позицию панели управления при любом изменении размера docked виджета
            updateControlPanelPosition();
        });
        
        // Настраиваем слушатель изменения состояния docked
        coordinatesWidget.setOnDockStateChangeListener((isDocked, isTop) -> {
            Log.d(TAG, "Coordinates dock state changed: docked=" + isDocked + ", top=" + isTop);
            
            // Перепозиционируем все docked виджеты
            BaseDockWidget.repositionAllDockedWidgets((ViewGroup) coordinatesWidget.getParent());
            
            updateControlPanelPosition();
            // Перекидываем системные паддинги в нужную сторону под новую
            // дока — чтобы под нав-баром/брови ничего не оставалось.
            reapplyInsetsToDocks();
        });
        
        // Устанавливаем виджет координат в dock-режим внизу экрана без тестовых данных
        coordinatesWidget.post(() -> {
            Log.d(TAG, "Setting coordinates widget to dock mode");
            coordinatesWidget.setDocked(true, false, 0, 0); // false = dock снизу
            coordinatesWidget.invalidate(); // Принудительная отрисовка
            // Только сейчас мы знаем сторону дока (bottom) — переприменяем
            // инсеты, чтобы виджет получил bottom padding под нав-бар
            // сразу, а не только после первого ресайза пользователем.
            reapplyInsetsToDocks();
        });
    }

    private void setupMessageAgesUpdater() {
        messageAgeHandler = new android.os.Handler(android.os.Looper.getMainLooper());
        messageAgeRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                    if (appCoordinator != null) {
                        // Получаем данные о возрасте сообщений через координатор
                        int gpsSec = appCoordinator.getSecondsSinceLastGPSMessage();
                        int aisSec = appCoordinator.getSecondsSinceLastAISMessage();
                        if (tvGpsAge != null) {
                            tvGpsAge.setText(gpsSec >= 0 ? ("GPS: " + gpsSec + " сек назад") : "GPS: --");
                            tvGpsAge.setTextColor(getAgeColor(gpsSec));
                        }
                        if (tvAisAge != null) {
                            tvAisAge.setText(aisSec >= 0 ? ("AIS: " + aisSec + " сек назад") : "AIS: --");
                            tvAisAge.setTextColor(getAgeColor(aisSec));
                        }
                        if (tvBleRssi != null) {
                            Integer rssi = appCoordinator.getLastBleRssi();
                            if (rssi != null) {
                                tvBleRssi.setText("BLE RSSI: " + rssi);
                                tvBleRssi.setTextColor(getRssiColor(rssi));
                            } else {
                                tvBleRssi.setText("BLE RSSI: --");
                                tvBleRssi.setTextColor(android.graphics.Color.parseColor("#F44336"));
                            }
                        }
                        if (tvBleBatt != null) {
                            Integer batt = appCoordinator.getLastBleBattery();
                            tvBleBatt.setText(batt != null ? ("BLE Batt: " + batt + "%") : "BLE Batt: --");
                        }
                    }
                } catch (Exception ignored) {}
                messageAgeHandler.postDelayed(this, 1000);
            }
        };
        // Стартуем после первичной инициализации
        messageAgeHandler.postDelayed(messageAgeRunnable, 1000);
    }

    private int getAgeColor(int seconds) {
        if (seconds < 0) {
            // Нет данных
            return Color.parseColor("#F44336"); // красный
        }
        if (seconds < 30) {
            return Color.parseColor("#4CAF50"); // зелёный
        } else if (seconds < 300) {
            return Color.parseColor("#FFC107"); // жёлтый
        } else {
            return Color.parseColor("#F44336"); // красный
        }
    }

    private int getRssiColor(int rssi) {
        // Типичные пороги: >= -60 dBm (сильный) — зелёный; >= -80 dBm (средний) — жёлтый; иначе — красный
        if (rssi >= -60) {
            return android.graphics.Color.parseColor("#4CAF50");
        } else if (rssi >= -80) {
            return android.graphics.Color.parseColor("#FFC107");
        } else {
            return android.graphics.Color.parseColor("#F44336");
        }
    }
    
    private void onUpdateCompass(float azimuth, List<AISVessel> nearbyVessels) {
        if (compassView != null) {
            compassView.setAzimuth(azimuth);
            compassView.updateNearbyVessels(nearbyVessels);
        }
    }
    
    /**
     * Инициализирует BottomSheet для отображения информации о нашем судне
     */
    private void initializeBottomSheet() {
        // Перенесено в BottomSheetsManager
        if (bottomSheetsManager == null && appCoordinator != null) {
            bottomSheetsManager = new BottomSheetsManager(this, appCoordinator);
            bottomSheetsManager.init();
        }
        
        // Инициализация BottomSheet для нашего судна
        // Управляется BottomSheetsManager
        
        // Настраиваем кнопку закрытия
        // Управляется BottomSheetsManager
        
        // Настраиваем поведение BottomSheet
        // Управляется BottomSheetsManager
        
        // Добавляем слушатель закрытия BottomSheet
        // Управляется BottomSheetsManager
        
        // Инициализация BottomSheet для AIS судов
        // Управляется BottomSheetsManager
        
        // Настраиваем кнопку закрытия для AIS BottomSheet
        // Управляется BottomSheetsManager
        
        // Настраиваем поведение AIS BottomSheet
        // Управляется BottomSheetsManager
        
        // Добавляем слушатель закрытия BottomSheet
        // Управляется BottomSheetsManager
    }
    
    /**
     * Настраивает управление экраном
     */
    private void setupScreenManagement() {
        // Загружаем настройку из SettingsManager
        if (settingsManager != null) {
            keepScreenOn = settingsManager.isKeepScreenOnEnabled();
        }
        
        // Применяем настройку
        setKeepScreenOn(keepScreenOn);
        
        Log.i(TAG, "Управление экраном настроено: keepScreenOn=" + keepScreenOn);
    }
    
    /**
     * Настраивает UI watchdog для отслеживания зависаний
     */
    private void setupUIWatchdog() {
        // ВАЖНО: watchdog не должен работать на UI Looper, иначе он не может детектить настоящий hang.
        // Поэтому тикер в фоне, а "pong" — маленькая задачка на UI.
        uiWatchdogHandler = new android.os.Handler(android.os.Looper.getMainLooper());
        lastUIUpdateTime = System.currentTimeMillis();
        lastUiPongUptimeMs = android.os.SystemClock.uptimeMillis();

        // Заглушка для обратной совместимости (на него ссылаются логи/tryRecoverFromUIHang)
        uiWatchdogRunnable = () -> {};

        try {
            uiWatchdogScheduler.scheduleAtFixedRate(() -> {
                final long pingUptime = android.os.SystemClock.uptimeMillis();
                try {
                    uiWatchdogHandler.post(() -> {
                        // "pong": если это исполнилось — UI Looper жив.
                        lastUiPongUptimeMs = android.os.SystemClock.uptimeMillis();
                        updateUIActivity();
                    });
                } catch (Throwable ignore) {}

                long sincePong = pingUptime - lastUiPongUptimeMs;
                if (sincePong > UI_TIMEOUT) {
                    // Не спамим логом каждую секунду.
                    if (pingUptime - lastUiHangLogUptimeMs > 10_000L) {
                        lastUiHangLogUptimeMs = pingUptime;
                        Log.e(TAG, "🚨 UI WATCHDOG: UI возможно завис (main looper не отвечает) " +
                                (sincePong / 1000) + " секунд");
                        dumpThreadStacksForDiagnosticsAsync();
                        // Recovery — только если UI хоть как-то отвечает (иначе бесполезно)
                        tryRecoverFromUIHang();
                    }
                }
            }, UI_WATCHDOG_INTERVAL, UI_WATCHDOG_INTERVAL, java.util.concurrent.TimeUnit.MILLISECONDS);
        } catch (Throwable t) {
            Log.e(TAG, "UI watchdog: не удалось запустить scheduler: " + t.getMessage(), t);
        }

        Log.i(TAG, "UI watchdog запущен (background)");
    }
    
    private final java.util.concurrent.ExecutorService watchdogExecutor =
            java.util.concurrent.Executors.newSingleThreadExecutor();
    private volatile long lastRecoveryAttemptMs = 0L;

    /**
     * Обновляет время последней активности UI
     */
    private void updateUIActivity() {
        long now = System.currentTimeMillis();
        long timeSinceLastUpdate = now - lastUIUpdateTime;
        lastUIUpdateTime = now;
        
        // Логируем если прошло больше 2 секунд с последнего обновления
        if (timeSinceLastUpdate > 2000) {
            Log.w(TAG, "⚠️ UI WATCHDOG: Долгая пауза в UI обновлениях: " + (timeSinceLastUpdate / 1000) + " сек");
        }
    }
    
    /**
     * Попытка восстановления после зависания UI
     */
    private void tryRecoverFromUIHang() {
        Log.w(TAG, "UI WATCHDOG: Попытка восстановления...");
        
        try {
            long now = System.currentTimeMillis();
            // Не долбим восстановлением каждую секунду — это само может стать причиной лагов
            if (now - lastRecoveryAttemptMs < 10_000L) {
                Log.i(TAG, "UI WATCHDOG: восстановление пропущено (throttle)");
                return;
            }
            lastRecoveryAttemptMs = now;

            // Диагностика: проверяем состояние handler'ов
            boolean watchdogActive = uiWatchdogHandler != null && uiWatchdogRunnable != null;
            boolean messageAgeActive = messageAgeHandler != null && messageAgeRunnable != null;
            boolean bottomSheetActive = bottomSheetsManager != null; // BottomSheetsManager сам управляет своими handler'ами
            boolean controlPanelActive = controlPanelUpdateHandler != null && controlPanelUpdateRunnable != null;
            
            Log.i(TAG, "UI WATCHDOG: Handler status - " +
                      "watchdog=" + watchdogActive + 
                      ", messageAge=" + messageAgeActive +
                      ", bottomSheet=" + bottomSheetActive +
                      ", controlPanel=" + controlPanelActive +
                      ", controlPanelCount=" + controlPanelUpdateCount);
            
            // ВАЖНО: никаких тяжёлых операций (System.gc) на UI-потоке.
            // Если нужно, можно поставить фоновой GC после лагов, но это диагностическая функция,
            // а не recovery, поэтому здесь намеренно ничего не делаем.
            
            // Проверяем состояние основных компонентов
            if (mapController != null && mapController.getCurrentMapInterface() == null) {
                Log.w(TAG, "UI WATCHDOG: mapInterface == null, переинициализируем карту");
                // Можно попробовать переинициализировать карту
            }
            
            if (appCoordinator == null) {
                Log.w(TAG, "UI WATCHDOG: appCoordinator == null");
            }
            
            // Если слишком много обновлений control panel, попробуем остановить
            if (controlPanelUpdateCount > 50) {
                Log.w(TAG, "UI WATCHDOG: Слишком много обновлений control panel (" + controlPanelUpdateCount + "/10сек), принудительно останавливаем");
                if (controlPanelUpdateHandler != null) {
                    controlPanelUpdateHandler.removeCallbacks(controlPanelUpdateRunnable);
                    controlPanelUpdatePending = false;
                    controlPanelUpdateCount = 0;
                }
            }
            
            // Обновляем время активности
            updateUIActivity();
            
            Log.i(TAG, "UI WATCHDOG: Восстановление завершено");
            
        } catch (Exception e) {
            Log.e(TAG, "UI WATCHDOG: Ошибка при восстановлении: " + e.getMessage(), e);
        }
    }
    
    /**
     * Диагностический дамп стеков главного и рабочих потоков
     */
    private void dumpThreadStacksForDiagnosticsAsync() {
        try {
            watchdogExecutor.execute(this::dumpThreadStacksForDiagnosticsBlocking);
        } catch (Throwable t) {
            Log.e(TAG, "UI WATCHDOG: не удалось запустить дамп стеков: " + t.getMessage(), t);
        }
    }

    private void dumpThreadStacksForDiagnosticsBlocking() {
        try {
            java.util.Map<Thread, StackTraceElement[]> all = Thread.getAllStackTraces();
            Thread main = Looper.getMainLooper().getThread();
            // Сначала главный поток
            if (main != null) {
                StackTraceElement[] st = all.get(main);
                Log.e(TAG, "===== MAIN THREAD STACK TRACE =====");
                if (st != null) {
                    for (StackTraceElement e : st) {
                        Log.e(TAG, "  at " + e.toString());
                    }
                }
            }
            // Затем несколько самых активных потоков по имени
            String[] interesting = new String[] {"AsyncTask", "RenderThread", "OkHttp", "GLThread", "pool-", "DefaultDispatcher"};
            for (java.util.Map.Entry<Thread, StackTraceElement[]> entry : all.entrySet()) {
                Thread t = entry.getKey();
                if (t == main) continue;
                String name = t.getName();
                boolean match = false;
                for (String key : interesting) {
                    if (name.contains(key)) { match = true; break; }
                }
                if (!match) continue;
                Log.w(TAG, "===== THREAD: " + name + " (" + t.getState() + ") =====");
                StackTraceElement[] st = entry.getValue();
                if (st != null) {
                    int count = 0;
                    for (StackTraceElement e : st) {
                        Log.w(TAG, "  at " + e.toString());
                        if (++count > 50) break; // ограничим длину
                    }
                }
            }
        } catch (Throwable t) {
            Log.e(TAG, "Ошибка дампа стеков: " + t.getMessage(), t);
        }
    }
    
    /**
     * Настраивает throttling для updateControlPanelPosition
     */
    private void setupControlPanelThrottling() {
        controlPanelUpdateHandler = new android.os.Handler(android.os.Looper.getMainLooper());
        controlPanelUpdateRunnable = () -> {
            controlPanelUpdatePending = false;
            updateControlPanelPositionSafe();
        };
    }
    
    /**
     * Безопасное обновление позиции панели управления с throttling
     */
    private void updateControlPanelPositionThrottled() {
        if (!controlPanelUpdatePending) {
            controlPanelUpdatePending = true;
            controlPanelUpdateHandler.removeCallbacks(controlPanelUpdateRunnable);
            controlPanelUpdateHandler.postDelayed(controlPanelUpdateRunnable, CONTROL_PANEL_UPDATE_DELAY);
        }
    }
    
    /**
     * Устанавливает режим работы экрана
     */
    private void setKeepScreenOn(boolean enabled) {
        if (enabled) {
            // Включаем режим "не засыпать"
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            Log.i(TAG, "Экран настроен на постоянную работу");
        } else {
            // Выключаем режим "не засыпать"
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
            Log.i(TAG, "Экран настроен на обычный режим (может засыпать)");
        }
    }
    
    /**
     * Переключает режим работы экрана
     */
    public void toggleKeepScreenOn() {
        keepScreenOn = !keepScreenOn;
        setKeepScreenOn(keepScreenOn);
        
        // Сохраняем настройку
        if (settingsManager != null) {
            settingsManager.setKeepScreenOnEnabled(keepScreenOn);
        }
        
        String message = keepScreenOn ? "Экран будет оставаться включенным" : "Экран может засыпать";
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        
        Log.i(TAG, "Режим экрана переключен: keepScreenOn=" + keepScreenOn);
    }
    
    /**
     * Переключает отображение морских знаков OpenSeaMap
     */
    public void toggleSeamarks() {
        if (settingsManager == null) {
            Log.w(TAG, "toggleSeamarks: settingsManager is null");
            return;
        }
        
        boolean currentState = settingsManager.isSeamarksEnabled();
        boolean newState = !currentState;
        
        // Сохраняем настройку
        settingsManager.setSeamarksEnabled(newState);
        
        // Применяем изменения на карте
        if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
            ((MapLibreMapImpl) mapController.getCurrentMapInterface()).updateAdditionalLayers();
        }
        
        String message = newState ? "Морские знаки включены" : "Морские знаки выключены";
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        
        Log.i(TAG, "Морские знаки переключены: enabled=" + newState);
    }
    
    private void initializeControllers() {
        // Инициализация менеджера настроек
        settingsManager = new SettingsManager(this);
        
        // Инициализация контроллера карты
        mapController = new MapController(this);
        
        // Инициализация главного координатора
        ControllersFactory controllersFactory = new DefaultControllersFactory();
        appCoordinator = controllersFactory.createAppCoordinator(this);

        // Init UI binders
        menuBinder = new MenuBinder(appCoordinator, settingsManager, new MenuBinder.MenuActions() {
            @Override public void toggleGPS() { MainActivity.this.toggleGPS(); }
            @Override public void toggleUDP() { MainActivity.this.toggleUDP(); }
            @Override public void clearAIS() { MainActivity.this.clearAIS(); }
            @Override public void togglePathTracking() { MainActivity.this.togglePathTracking(); }
            @Override public void testForegroundService() { MainActivity.this.testForegroundService(); }
            @Override public void toggleKeepScreenOn() { MainActivity.this.toggleKeepScreenOn(); }
            @Override public void toggleSeamarks() { MainActivity.this.toggleSeamarks(); }
        });
        // Не используем BottomSheetsBinder, оставляем рабочую реализацию в MainActivity
        permissionsBinder = new PermissionsBinder(this);
        appCoordinator.setMapController(mapController);
        
        // Инициализация контроллера компаса
        compassController = new CompassController(this);
        
        // Устанавливаем callback для обновления UI

        // Запускаем Foreground Service для фоновых обновлений AIS/GPS
        startForegroundService();
        appCoordinator.setListener(new AppCoordinator.AppCoordinatorListener() {
            @Override
            public void onVesselPositionUpdated(Vessel vessel) {
                updateUIActivity(); // Обновляем watchdog
                updateVesselPositionUI(vessel);
                
                // Троттлинг обновлений компаса
                lastCompassVessel = vessel;
                uiThrottleHandler.removeCallbacks(compassUpdateRunnable);
                uiThrottleHandler.postDelayed(compassUpdateRunnable, UI_UPDATE_THROTTLE_MS);
            }
            
            @Override
            public void onGPSQualityUpdated(Vessel vessel) {
                updateGPSQualityUI(vessel);
            }
            
            @Override
            public void onShowOwnVesselBottomSheet() {
                Log.i(TAG, "onShowOwnVesselBottomSheet callback получен в MainActivity");
                if (bottomSheetsManager == null) {
                    bottomSheetsManager = new BottomSheetsManager(MainActivity.this, appCoordinator);
                    bottomSheetsManager.init();
                }
                bottomSheetsManager.showOwnVessel();
            }
            
            @Override
            public void onShowAISVesselInfo(AISVessel vessel) {
                if (bottomSheetsManager == null) {
                    bottomSheetsManager = new BottomSheetsManager(MainActivity.this, appCoordinator);
                    bottomSheetsManager.init();
                }
                bottomSheetsManager.showAISVessel(vessel);
            }
            
            @Override
            public void onUpdateCompass(float azimuth, List<AISVessel> nearbyVessels) {
                updateUIActivity(); // Обновляем watchdog
                if (compassView != null) {
                    compassView.setAzimuth(azimuth);
                    compassView.updateNearbyVessels(nearbyVessels);
                }
            }
        });
        refreshMapRotationButtonDescription();
    }
    
    private void startControllers() {
        // Загружаем настройки и применяем их
        applySettings();
        
        // Запускаем все контроллеры через координатор
        appCoordinator.startAllControllers();
        
        // Запускаем компас
        compassController.startCompass();
    }
    
    /**
     * Обновляет статус в UI
     */
    private void updateStatusUI() {
        // Обновляем статус в UI
//        TextView tvStatus = findViewById(R.id.tv_status);
//        TextView tvAisCount = findViewById(R.id.tv_ais_count);
//
//        if (tvStatus != null) {
//            tvStatus.setText("Статус: GPS активен, UDP готов");
//        }
//
//        if (tvAisCount != null) {
//            tvAisCount.setText("AIS суда: 0");
//        }
    }
    
    /**
     * Обновляет позицию судна в UI
     */
    private void updateVesselPositionUI(Vessel vessel) {
        if (isFinishing() || isDestroyed()) return;
        
        runOnUiThread(() -> {
            try {
                updateUIActivity(); // Обновляем watchdog
                
                if (vessel == null) return;
                
                // Троттлинг обновлений координатного виджета
                lastCoordinatesVessel = vessel;
                uiThrottleHandler.removeCallbacks(coordinatesUpdateRunnable);
                uiThrottleHandler.postDelayed(coordinatesUpdateRunnable, UI_UPDATE_THROTTLE_MS);
                
                // Обновляем BottomSheet через менеджер, если открыт
                if (bottomSheetsManager != null) {
                    bottomSheetsManager.updateOwnVesselUI();
                }
            } catch (Exception e) {
                Log.e(TAG, "Ошибка в updateVesselPositionUI: " + e.getMessage(), e);
            }
        });
    }
    
    /**
     * Обновляет качество GPS в UI
     */
    private void updateGPSQualityUI(Vessel vessel) {
        if (isFinishing() || isDestroyed()) return;
        
        runOnUiThread(() -> {
            try {
                updateUIActivity(); // Обновляем watchdog
                
                if (vessel == null) return;
                
                // Обновляем BottomSheet через менеджер, если открыт
                if (bottomSheetsManager != null) {
                    bottomSheetsManager.updateOwnVesselUI();
                }
            } catch (Exception e) {
                Log.e(TAG, "Ошибка в updateGPSQualityUI: " + e.getMessage(), e);
            }
        });
    }
    
    private void toggleUDP() {
        // TODO: Добавить методы для управления UDP в AppCoordinator
        Toast.makeText(this, "UDP управление через настройки", Toast.LENGTH_SHORT).show();
        
        // Обновляем заголовок меню
        invalidateOptionsMenu();
    }
    
    private void toggleGPS() {
        // TODO: Добавить методы для управления GPS в AppCoordinator
        Toast.makeText(this, "GPS управление через настройки", Toast.LENGTH_SHORT).show();
        
        // Обновляем заголовок меню
        invalidateOptionsMenu();
    }
    
    private void centerOnVessel() {
        appCoordinator.centerOnOwnVessel();
        Toast.makeText(this, "Карта центрирована на судне", Toast.LENGTH_SHORT).show();
    }
    
    private static float normalizeBearingTo360(double deg) {
        double x = deg % 360.0;
        if (x < 0) x += 360.0;
        return (float) x;
    }

    /**
     * Три режима: по магнитному компасу, по курсу (COG), вручную
     * (север вверх при переключении, дальше — только жесты пользователя).
     * Кнопка циклически переключает: компас → курс → вручную → …
     */
    private void cycleMapRotationMode() {
        if (settingsManager == null) return;
        String mode = settingsManager.cycleMapRotationMode();
        refreshMapRotationButtonDescription();
        if (mapController == null || mapController.getCurrentMapInterface() == null) {
            Toast.makeText(this, "Режим карты сохранён — применится после загрузки карты",
                    Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            MapInterface map = mapController.getCurrentMapInterface();
            applyMapRotationForMode(map, mode, true);
        } catch (Exception e) {
            Log.w(TAG, "cycleMapRotationMode: " + e.getMessage());
        }
    }

    private void applyMapRotationForMode(MapInterface map, String mode, boolean showShortToast) {
        if (map == null || mode == null) return;
        if (SettingsManager.MAP_ROTATION_MANUAL.equals(mode)) {
            map.setBearing(0f);
            if (showShortToast) {
                Toast.makeText(this, "Карта: вручную (север вверх, дальше — жестом)",
                        Toast.LENGTH_LONG).show();
            }
            return;
        }
        if (appCoordinator == null) return;
        Vessel own = appCoordinator.getOwnVessel();
        if (own == null) {
            if (showShortToast) {
                Toast.makeText(this, "Нет данных собственного судна", Toast.LENGTH_SHORT).show();
            }
            return;
        }
        if (SettingsManager.MAP_ROTATION_COMPASS.equals(mode)) {
            float b = normalizeBearingTo360(own.getMagneticCompass());
            map.setBearing(b);
            if (showShortToast) {
                Toast.makeText(this,
                        String.format(java.util.Locale.US, "По компасу (%.0f°)", b),
                        Toast.LENGTH_SHORT).show();
            }
            return;
        }
        if (SettingsManager.MAP_ROTATION_COURSE.equals(mode)) {
            if (Double.isNaN(own.getCourse())) {
                if (showShortToast) {
                    Toast.makeText(this, "Пока нет курса (COG)", Toast.LENGTH_SHORT).show();
                }
                return;
            }
            float b = normalizeBearingTo360(own.getCourse());
            map.setBearing(b);
            if (showShortToast) {
                Toast.makeText(this,
                        String.format(java.util.Locale.US, "По курсу COG (%.0f°)", b),
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void applyAutoMapBearingIfNeeded(MapInterface map) {
        if (settingsManager == null || appCoordinator == null || map == null) return;
        String mode = settingsManager.getMapRotationMode();
        if (SettingsManager.MAP_ROTATION_MANUAL.equals(mode)) {
            return;
        }
        try {
            Vessel own = appCoordinator.getOwnVessel();
            if (own == null) return;
            if (SettingsManager.MAP_ROTATION_COMPASS.equals(mode)) {
                map.setBearing(normalizeBearingTo360(own.getMagneticCompass()));
            } else if (SettingsManager.MAP_ROTATION_COURSE.equals(mode)
                    && !Double.isNaN(own.getCourse())) {
                map.setBearing(normalizeBearingTo360(own.getCourse()));
            }
        } catch (Exception ignore) {}
    }

    private void refreshMapRotationButtonDescription() {
        if (btnMapOrientation == null || settingsManager == null) return;
        String m = settingsManager.getMapRotationMode();
        if (SettingsManager.MAP_ROTATION_COMPASS.equals(m)) {
            btnMapOrientation.setContentDescription("Карта по компасу (нажмите — смена режима)");
        } else if (SettingsManager.MAP_ROTATION_COURSE.equals(m)) {
            btnMapOrientation.setContentDescription("Карта по курсу COG (нажмите — смена режима)");
        } else {
            btnMapOrientation.setContentDescription("Карта вручную, север вверх (нажмите — смена режима)");
        }
    }
    
    private void togglePathTracking() {
        boolean currentState = settingsManager.isPathTrackingEnabled();
        boolean newState = !currentState;
        
        settingsManager.setPathTrackingEnabled(newState);
        
        // Обновляем состояние в карте
        if (mapController.getCurrentMapInterface() instanceof com.grigowashere.aismap.maps.YandexMapImpl) {
            ((com.grigowashere.aismap.maps.YandexMapImpl) mapController.getCurrentMapInterface()).setPathTrackingEnabled(newState);
        }
        
        String message = newState ? "Отслеживание путей включено" : "Отслеживание путей выключено";
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
        
        // Обновляем меню
        invalidateOptionsMenu();
    }
    
    /**
     * Очищает трекер пути собственного судна
     */
    private void clearVesselPath() {
        try {
            Log.i(TAG, "clearVesselPath() вызван");
            
            if (mapController.getCurrentMapInterface() != null) {
                Log.i(TAG, "Очищаем путь в карте");
                // Очищаем путь в карте
                mapController.getCurrentMapInterface().clearVesselPath();
                
                // Также очищаем VesselPathController при наличии в координаторе
                if (appCoordinator != null) {
                    Log.i(TAG, "Очищаем VesselPathController в AppCoordinator");
                    appCoordinator.clearVesselPath();
                } else {
                    Log.w(TAG, "AppCoordinator is null, не можем очистить VesselPathController");
                }
                
                Toast.makeText(this, "Трекер пути очищен", Toast.LENGTH_SHORT).show();
                Log.i(TAG, "Трекер пути собственного судна очищен");
            } else {
                Toast.makeText(this, "Карта не инициализирована", Toast.LENGTH_SHORT).show();
                Log.w(TAG, "Попытка очистки пути при неинициализированной карте");
            }
        } catch (Exception e) {
            Log.e(TAG, "Ошибка при очистке пути: " + e.getMessage(), e);
            Toast.makeText(this, "Ошибка при очистке пути", Toast.LENGTH_SHORT).show();
        }
    }
    
    private void showSettings() {
        Intent intent = new Intent(this, SettingsActivity.class);
        startActivityForResult(intent, SETTINGS_REQUEST_CODE);
    }

    private void openAisTargets() {
        Intent intent = new Intent(this, AisTargetsActivity.class);
        startActivity(intent);
    }

    /**
     * Переключает источник координат между BLE Hub и Android GPS «на лету»,
     * обновляет иконку кнопки и уведомляет AppCoordinator.
     */
    private void toggleGpsSource() {
        if (settingsManager == null) return;
        String next = settingsManager.toggleGpsSource();
        refreshGpsSourceButtonIcon();
        if (appCoordinator != null) {
            appCoordinator.applyGpsSourceChange();
        }
        String label = SettingsManager.GPS_SOURCE_HUB.equals(next)
                ? "Источник: AIS Hub (BLE)"
                : "Источник: Android GPS";
        Toast.makeText(this, label, Toast.LENGTH_SHORT).show();
    }

    /**
     * Навешивает единый листенер WindowInsets на корень активити и рассыпает
     * рассчитанные инсеты (system bars + display cutout) по трём ключевым
     * элементам верхнего слоя: компас, координатный виджет, боковая панель
     * управления. Благодаря этому контент не прячется за статус-баром,
     * нав-баром и вырезами камеры, а фоновые прямоугольники продолжают
     * доходить до физических краёв экрана.
     */
    private Insets lastSysInsets = Insets.NONE;

    private void installMainUiInsets() {
        View root = findViewById(R.id.main_root);
        if (root == null) return;
        ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
            Insets sys = insets.getInsets(
                    WindowInsetsCompat.Type.systemBars()
                            | WindowInsetsCompat.Type.displayCutout());
            lastSysInsets = sys;
            applyInsetsToDocks(sys);
            if (controlPanel != null) {
                ViewGroup.LayoutParams rawLp = controlPanel.getLayoutParams();
                if (rawLp instanceof android.widget.RelativeLayout.LayoutParams) {
                    android.widget.RelativeLayout.LayoutParams lp =
                            (android.widget.RelativeLayout.LayoutParams) rawLp;
                    int newRight = sys.right + Math.round(getResources().getDisplayMetrics().density * 8);
                    if (lp.rightMargin != newRight) {
                        lp.rightMargin = newRight;
                        controlPanel.setLayoutParams(lp);
                    }
                }
            }
            return insets;
        });
        // На первом layout инсеты иногда ещё не выданы системой: мы выставляем
        // слушатель, но callback не приходит до prewarm. Поэтому просим
        // отложенно — через post — чтобы попасть после attach и первого layout.
        ViewCompat.requestApplyInsets(root);
        root.post(() -> ViewCompat.requestApplyInsets(root));
        root.postDelayed(() -> ViewCompat.requestApplyInsets(root), 200);
        // Маргин control_panel по вертикали пересчитываем от фактической
        // высоты доков (она уже включает системные паддинги), чтобы панель
        // никогда не наползала на компас/координаты при ресайзе.
        View.OnLayoutChangeListener relayoutControlPanel = (v2, l, t, r, b, ol, ot, orr, ob) -> {
            if (controlPanel == null) return;
            ViewGroup.LayoutParams rawLp = controlPanel.getLayoutParams();
            if (!(rawLp instanceof android.widget.RelativeLayout.LayoutParams)) return;
            android.widget.RelativeLayout.LayoutParams lp =
                    (android.widget.RelativeLayout.LayoutParams) rawLp;
            int dp8 = Math.round(getResources().getDisplayMetrics().density * 8);
            int compassH = compassView != null ? compassView.getHeight() : 0;
            int coordsH = coordinatesWidget != null ? coordinatesWidget.getHeight() : 0;
            int newTop = compassH + dp8;
            int newBottom = coordsH + dp8;
            if (lp.topMargin != newTop || lp.bottomMargin != newBottom) {
                lp.topMargin = newTop;
                lp.bottomMargin = newBottom;
                controlPanel.setLayoutParams(lp);
            }
        };
        if (compassView != null) compassView.addOnLayoutChangeListener(relayoutControlPanel);
        if (coordinatesWidget != null) coordinatesWidget.addOnLayoutChangeListener(relayoutControlPanel);
    }

    /**
     * Применяет системные инсеты к компасу и координатному виджету в
     * зависимости от того, к какой стороне экрана они пристыкованы.
     * Если док у верхнего края — добавляем верхний паддинг (статус-бар,
     * вырез камеры). Если у нижнего — добавляем нижний паддинг под нав-бар.
     * Боковые паддинги даём всегда (landscape-камеры).
     */
    private void applyInsetsToDocks(Insets sys) {
        if (compassView != null) {
            boolean top = compassView.isDockTop();
            compassView.setPadding(sys.left, top ? sys.top : 0,
                    sys.right, top ? 0 : sys.bottom);
        }
        if (coordinatesWidget != null) {
            boolean top = coordinatesWidget.isDockTop();
            coordinatesWidget.setPadding(sys.left, top ? sys.top : 0,
                    sys.right, top ? 0 : sys.bottom);
        }
    }

    /** Переприменяет уже собранные инсеты (вызывать при смене стороны дока). */
    private void reapplyInsetsToDocks() {
        applyInsetsToDocks(lastSysInsets);
        View root = findViewById(R.id.main_root);
        if (root != null) ViewCompat.requestApplyInsets(root);
    }

    private void refreshGpsSourceButtonIcon() {
        if (btnGpsSource == null || settingsManager == null) return;
        int icon = settingsManager.isGpsFromHub()
                ? R.drawable.ic_gps_source_hub
                : R.drawable.ic_gps_source_android;
        btnGpsSource.setImageResource(icon);
        btnGpsSource.setContentDescription(
                settingsManager.isGpsFromHub()
                        ? "Источник: AIS Hub"
                        : "Источник: Android GPS");
    }

    /**
     * Переключает отображение курсора на карте и сохраняет состояние
     */
    private void toggleCursor() {
        if (mapController == null || mapController.getCurrentMapInterface() == null || settingsManager == null) return;
        boolean enabled = settingsManager.isCursorEnabled();
        boolean newState = !enabled;
        settingsManager.setCursorEnabled(newState);
        try {
            if (newState) {
                mapController.getCurrentMapInterface().showCursor();
                mapController.getCurrentMapInterface().updateCursorFromMapCenter();
                if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                    ((MapLibreMapImpl) mapController.getCurrentMapInterface()).forceCheckAisVesselUnderCursor();
                }
                Toast.makeText(this, "Курсор включён", Toast.LENGTH_SHORT).show();
            } else {
                mapController.getCurrentMapInterface().hideCursor();
                Toast.makeText(this, "Курсор выключен", Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            Log.w(TAG, "toggleCursor error: " + e.getMessage());
        }
    }
    
    /**
     * Обновляет позицию панели управления с throttling
     */
    private void updateControlPanelPosition() {
        updateControlPanelPositionThrottled();
    }
    
    /**
     * Безопасное обновление позиции панели управления (вызывается через throttling)
     */
    private void updateControlPanelPositionSafe() {
        if (controlPanel == null) return;
        
        try {
            updateUIActivity(); // Обновляем watchdog
            
            // Диагностика: считаем количество обновлений
            controlPanelUpdateCount++;
            long now = System.currentTimeMillis();
            if (now - lastControlPanelUpdateTime > 10000) { // каждые 10 секунд
                Log.d(TAG, "Control panel updates count: " + controlPanelUpdateCount + " за последние 10 сек");
                controlPanelUpdateCount = 0;
                lastControlPanelUpdateTime = now;
            }
            
            // Получаем параметры layout
            android.widget.RelativeLayout.LayoutParams params = 
                (android.widget.RelativeLayout.LayoutParams) controlPanel.getLayoutParams();
            if (params == null) return;
                
            int defaultMargin = dpToPx(16);
            int topMargin = defaultMargin;
            int bottomMargin = defaultMargin;
            
            // Проверяем compassView
            int compassHeight = 0;
            if (compassView != null && compassView.isDocked()) {
                compassHeight = compassView.getHeight();
                if (compassHeight <= 0) return; // Избегаем 0 размера, который может вызвать перестройку
            
                if (compassView.isDockTop()) {
                    topMargin = compassHeight + dpToPx(8);
                } else {
                    bottomMargin = compassHeight + dpToPx(8);
                }
            }
            
            // Проверяем coordinatesWidget
            int coordinatesHeight = 0;
            if (coordinatesWidget != null && coordinatesWidget.isDocked()) {
                coordinatesHeight = coordinatesWidget.getHeight();
                if (coordinatesHeight <= 0) return; // Избегаем 0 размера
                
                if (coordinatesWidget.isDockTop()) {
                    topMargin = Math.max(topMargin, coordinatesHeight + dpToPx(8));
                } else {
                    bottomMargin = Math.max(bottomMargin, coordinatesHeight + dpToPx(8));
                }
            }
            
            // Применяем изменения только если они отличаются от текущих
            if (params.topMargin != topMargin || params.bottomMargin != bottomMargin) {
                params.topMargin = topMargin;
                params.bottomMargin = bottomMargin;
                controlPanel.setLayoutParams(params);
                
                // Минимальное логирование в production
                Log.d(TAG, "Control panel updated: top=" + topMargin + ", bottom=" + bottomMargin);
            }
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка при обновлении позиции панели управления: " + e.getMessage(), e);
        }
    }
    

    
    private void clearAIS() {
        appCoordinator.clearAISVessels();
        Toast.makeText(this, "AIS суда очищены", Toast.LENGTH_SHORT).show();
    }
    
    /**
     * Конвертирует dp в px
     */
    private int dpToPx(int dp) {
        return (int) (dp * getResources().getDisplayMetrics().density);
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        
        // MapLibre lifecycle
        if (mapView != null) {
            mapView.onStart();
        }
        
        // Запускаем карту через контроллер
        if (mapController != null) {
            Log.i(TAG, "Запускаем карту...");
            mapController.startMap();
            
            // Инициализируем карту
            Log.i(TAG, "Инициализируем карту...");
            MapInterface mapInterface = mapController.initializeMapLibre(mapView);
            Log.i(TAG, "mapInterface получен: " + (mapInterface != null ? "успешно" : "null"));
            
            // Устанавливаем интерфейс карты в главный контроллер
            if (mapInterface != null) {
                // Сначала создаем UI Coordinator
                uiCoordinator = new UIRenderingCoordinator(mapInterface);
                Log.i(TAG, "UIRenderingCoordinator создан");
                
                // Подписываем UIRenderingCoordinator на изменения MapInterface
                mapController.addMapInterfaceChangeListener(uiCoordinator);
                Log.i(TAG, "UIRenderingCoordinator подписан на изменения MapInterface");
                
                // Устанавливаем UI Coordinator как notifier для AppCoordinator
                appCoordinator.setUIDataChangeNotifier(uiCoordinator);
                Log.i(TAG, "UIDataChangeNotifier установлен в AppCoordinator");
                
                // AppCoordinator уже подключен к MapController при инициализации
                // setMapInterface больше не нужен, так как стратегия карты централизована
                Log.i(TAG, "AppCoordinator подключен к MapController");
                
                // Принудительно выполняем pending операции для восстановления данных
                uiCoordinator.flushPendingOperations();
                Log.i(TAG, "Pending операции выполнены для восстановления маркеров");
                
                
                // Инициализируем курсор согласно настройкам
                initializeCursor();
                
                // Устанавливаем VesselPathController и AppCoordinator в MapController
                if (appCoordinator != null) {
                    VesselPathController pathController = appCoordinator.getPathController();
                    if (pathController != null) {
                        mapController.setVesselPathController(pathController);
                        Log.i(TAG, "VesselPathController установлен в MapController");
                    }
                    mapController.setAppCoordinator(appCoordinator);
                    Log.i(TAG, "AppCoordinator установлен в MapController");
                }
                
                mapController.getCurrentMapInterface().initialize();
                Log.i(TAG, "Карта инициализирована");

                // Обновляем размеры экрана для курсора после инициализации
                if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                    ((MapLibreMapImpl) mapController.getCurrentMapInterface()).updateScreenDimensions();
                }

                // Применяем отложенное центрирование, если было
                applyPendingCenterIfAny();

                // Старт: инициализируем ownVessel координатами устройства и центрируемся на нём
                // НО ТОЛЬКО при первом запуске приложения и если нет интента центрирования на сторонний корабль
                try {
                    Intent currentIntent = getIntent();
                    boolean hasExternalCenterIntent = currentIntent != null && 
                        currentIntent.hasExtra("center_lat") && currentIntent.hasExtra("center_lon");
                    
                    if (isFirstStart && !hasExternalCenterIntent && settingsManager != null && settingsManager.isStartCenterOnLastEnabled()) {
                        Log.i(TAG, "Первый запуск: инициализируем ownVessel и центрируемся");
                        android.location.LocationManager lm = (android.location.LocationManager) getSystemService(android.content.Context.LOCATION_SERVICE);
                        android.location.Location lastLoc = null;
                        if (lm != null) {
                            // Пробуем GPS, затем NETWORK
                            try { lastLoc = lm.getLastKnownLocation(android.location.LocationManager.GPS_PROVIDER); } catch (Exception ignore) {}
                            if (lastLoc == null) {
                                try { lastLoc = lm.getLastKnownLocation(android.location.LocationManager.NETWORK_PROVIDER); } catch (Exception ignore) {}
                            }
                        }
                        if (lastLoc != null) {
                            double lat = lastLoc.getLatitude();
                            double lon = lastLoc.getLongitude();
                            Log.i(TAG, "Первый запуск: seed ownVessel из Android LastKnownLocation " + lat + "," + lon);
                            if (appCoordinator != null) {
                                appCoordinator.seedOwnVesselFromDeviceLocation(lat, lon);
                                appCoordinator.centerOnOwnVessel();
                                // Повторим центрирование чуть позже, когда стиль точно загрузится
                                mapView.postDelayed(() -> {
                                    try {
                                        appCoordinator.centerOnOwnVessel();
                                    } catch (Exception ignore) {}
                                }, 500);
                            } else if (mapController.getCurrentMapInterface() != null) {
                                // fallback
                                mapController.getCurrentMapInterface().centerOnPosition(lat, lon);
                            }
                            float startZoom = settingsManager.getStartZoomLevel();
                            if (startZoom > 0f) {
                                mapController.getCurrentMapInterface().setZoom(startZoom);
                            }
                        } else {
                            Log.i(TAG, "Первый запуск: LastKnownLocation отсутствует");
                        }
                        // Отмечаем, что первый запуск завершен
                        isFirstStart = false;
                    } else if (hasExternalCenterIntent) {
                        Log.i(TAG, "Первый запуск с интентом центрирования на сторонний корабль - пропускаем центрирование на собственный");
                        // Отмечаем, что первый запуск завершен
                        isFirstStart = false;
                    } else if (!isFirstStart) {
                        Log.i(TAG, "Не первый запуск - пропускаем центрирование на собственный корабль");
                    } else {
                        Log.i(TAG, "Первый запуск, но центрирование отключено в настройках");
                        // Отмечаем, что первый запуск завершен
                        isFirstStart = false;
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Ошибка стартовой инициализации позиции/центрирования: " + e.getMessage(), e);
                }
                
                // Отслеживание путей для MapLibre будет добавлено позже
                
                // Проверяем, что все настроено правильно
                Log.i(TAG, "Проверяем настройку карты...");
                
                // Дополнительная проверка обработчиков кликов
                Log.i(TAG, "Проверяем обработчики кликов...");
                if (mapController.getCurrentMapInterface() instanceof com.grigowashere.aismap.maps.YandexMapImpl) {
                    com.grigowashere.aismap.maps.YandexMapImpl yandexMap = (com.grigowashere.aismap.maps.YandexMapImpl) mapController.getCurrentMapInterface();
                    yandexMap.refreshMarkerClickListeners();
                    Log.i(TAG, "Обработчики кликов обновлены");
                }
            } else {
                Log.e(TAG, "Не удалось получить mapInterface!");
            }
        }
        
        // Обрабатываем возможный интент центрирования
        handleCenterIntentIfAny(getIntent());

        // Восстанавливаем курсор после возврата в активность согласно настройке
        if (mapController.getCurrentMapInterface() != null) {
            boolean cursorEnabled = settingsManager.isCursorEnabled();
            if (cursorEnabled) {
                mapController.getCurrentMapInterface().showCursor();
                mapController.getCurrentMapInterface().updateCursorFromMapCenter();
                if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                    ((MapLibreMapImpl) mapController.getCurrentMapInterface()).forceCheckAisVesselUnderCursor();
                }
            } else {
                // Явно скрываем при возвращении, если выключен в настройках
                mapController.getCurrentMapInterface().hideCursor();
            }
        }

        // Проверяем разрешения и запускаем контроллеры
        checkPermissions();

        // Перезапускаем цикл поворота кнопок после возврата в активити
        startCompassButtonsLoop();

        // Старт FPS трекера
        if (tvFps != null) {
            android.view.Choreographer.getInstance().removeFrameCallback(fpsCallback);
            android.view.Choreographer.getInstance().postFrameCallback(fpsCallback);
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        handleCenterIntentIfAny(intent);
    }

    private void handleCenterIntentIfAny(Intent intent) {
        if (intent == null) return;
        if (intent.hasExtra("center_lat") && intent.hasExtra("center_lon")) {
            double lat = intent.getDoubleExtra("center_lat", 0);
            double lon = intent.getDoubleExtra("center_lon", 0);
            Log.i(TAG, "Получен интент центрирования: lat=" + lat + ", lon=" + lon);
            if (lat != 0 || lon != 0) {
                if (mapController.getCurrentMapInterface() != null) {
                    Log.i(TAG, "Центрируем карту немедленно");
                    mapController.getCurrentMapInterface().centerOnPosition(lat, lon);
                } else {
                    // Сохраняем для применения после инициализации карты
                    Log.i(TAG, "Сохраняем координаты для отложенного центрирования");
                    pendingCenterLat = lat;
                    pendingCenterLon = lon;
                }
            }
            // Сбрасываем, чтобы не повторялось при поворотах
            intent.removeExtra("center_lat");
            intent.removeExtra("center_lon");
            intent.removeExtra("center_mmsi");
        }
    }

    private void applyPendingCenterIfAny() {
        if (mapController.getCurrentMapInterface() == null) return;
        if (pendingCenterLat != null && pendingCenterLon != null) {
            Log.i(TAG, "Применяем отложенное центрирование: lat=" + pendingCenterLat + ", lon=" + pendingCenterLon);
            mapController.getCurrentMapInterface().centerOnPosition(pendingCenterLat, pendingCenterLon);
            pendingCenterLat = null;
            pendingCenterLon = null;
        }
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        
        // Очищаем информацию о AIS судне при паузе активности
        if (mapController.getCurrentMapInterface() != null) {
            mapController.getCurrentMapInterface().clearAisVesselInfo();
            // Принудительно удаляем overlay курсора, чтобы не накапливался между активити
            if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                ((MapLibreMapImpl) mapController.getCurrentMapInterface()).removeCursorOverlay();
            }
        }
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        
        // MapLibre lifecycle
        if (mapView != null) {
            mapView.onStop();
        }
        
        // Останавливаем карту
        if (mapController.getCurrentMapInterface() != null) {
            // Очищаем информацию о AIS судне перед остановкой карты
            mapController.getCurrentMapInterface().clearAisVesselInfo();
            mapController.getCurrentMapInterface().cleanup();
        }
        
        // Очищаем UI Coordinator
        if (uiCoordinator != null) {
            uiCoordinator.cleanup();
        }
        
        // Очищаем троттлинг
        if (uiThrottleHandler != null) {
            uiThrottleHandler.removeCallbacks(compassUpdateRunnable);
            uiThrottleHandler.removeCallbacks(coordinatesUpdateRunnable);
            uiThrottleHandler.removeCallbacks(compassButtonRotationRunnable);
        }

        // Останавливаем FPS трекер
        try { android.view.Choreographer.getInstance().removeFrameCallback(fpsCallback); } catch (Exception ignore) {}
        
        // Не останавливаем слушатели здесь, чтобы UDP продолжал работать в фоне
        // if (appController != null) {
        // 	appController.stopAllListeners();
        // }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        // MapLibre lifecycle
        if (mapView != null) {
            mapView.onDestroy();
        }
        
        // Очищаем информацию о AIS судне при уничтожении активности
        if (mapController.getCurrentMapInterface() != null) {
            mapController.getCurrentMapInterface().clearAisVesselInfo();
        }
        
        // Останавливаем обновление времени
        if (bottomSheetsManager != null) {
            bottomSheetsManager.stopTimeUpdate();
        }
        
        // Останавливаем автоматическое обновление BottomSheet
        if (bottomSheetsManager != null) {
            bottomSheetsManager.stopBottomSheetAutoUpdate();
        }
        
        // Останавливаем обновление возраста сообщений
        if (messageAgeHandler != null && messageAgeRunnable != null) {
            messageAgeHandler.removeCallbacks(messageAgeRunnable);
            Log.i(TAG, "messageAgeHandler остановлен");
        }
        
        // Останавливаем UI watchdog
        if (uiWatchdogHandler != null && uiWatchdogRunnable != null) {
            uiWatchdogHandler.removeCallbacks(uiWatchdogRunnable);
            Log.i(TAG, "UI watchdog остановлен");
        }
        try { uiWatchdogScheduler.shutdownNow(); } catch (Throwable ignore) {}
        
        // Останавливаем throttling handler для control panel
        if (controlPanelUpdateHandler != null && controlPanelUpdateRunnable != null) {
            controlPanelUpdateHandler.removeCallbacks(controlPanelUpdateRunnable);
            Log.i(TAG, "Control panel throttling остановлен");
        }
        
        // Останавливаем магнитный компас
        if (compassController != null) {
            compassController.stopCompass();
        }
        
        // Освобождаем ресурсы
        if (appCoordinator != null) {
            appCoordinator.cleanup();
        }
        
        if (mapController != null) {
            mapController.cleanup();
        }
        
        // Останавливаем LogSender
        LogSender.shutdown();
        
        // Останавливаем форграунд сервис
        stopForegroundService();
    }
    
    @Override
    public void onConfigurationChanged(android.content.res.Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        
        // Обрабатываем изменения конфигурации (например, поворот экрана)
        if (mapController.getCurrentMapInterface() != null) {
            // Обновляем размеры экрана для курсора после поворота
            if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                ((MapLibreMapImpl) mapController.getCurrentMapInterface()).updateScreenDimensions();
            }
        }
    }
    
    /**
     * Проверяет необходимые разрешения
     */
    private void checkPermissions() {
        // Через PermissionsBinder, чтобы централизовать работу с разрешениями
        if (permissionsBinder == null) {
            permissionsBinder = new PermissionsBinder(this);
        }
        boolean ok = permissionsBinder.ensurePermission(Manifest.permission.ACCESS_FINE_LOCATION, PERMISSION_REQUEST_CODE);
        if (ok) {
            startControllers();
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (permissionsBinder != null &&
            permissionsBinder.handleOnRequestPermissionsResult(
                requestCode,
                permissions,
                grantResults,
                PERMISSION_REQUEST_CODE,
                this::startControllers,
                () -> Toast.makeText(this, "Для работы приложения необходимо разрешение на доступ к местоположению", Toast.LENGTH_LONG).show())) {
            return;
        }
        if (permissionsBinder != null &&
            permissionsBinder.handleOnRequestPermissionsResult(
                requestCode,
                permissions,
                grantResults,
                NOTIFICATION_PERMISSION_REQUEST_CODE,
                this::startForegroundService,
                () -> {
                    android.util.Log.w(TAG, "Разрешение на уведомления не получено");
                    Toast.makeText(this, "Для работы в фоне необходимо разрешение на уведомления", Toast.LENGTH_LONG).show();
                })) {
            return;
        }
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        if (requestCode == SETTINGS_REQUEST_CODE) {
            if (resultCode == RESULT_OK && data != null) {
                boolean settingsChanged = data.getBooleanExtra("settings_changed", false);
                boolean needsRestart = data.getBooleanExtra("needs_restart", false);
                boolean clearVesselPath = data.getBooleanExtra("clear_vessel_path", false);
                boolean cursorEnabled = data.getBooleanExtra("cursor_enabled", false);
                
                if (clearVesselPath) {
                    Log.i(TAG, "Запрошена очистка трекера пути");
                    clearVesselPath();
                }
                
                if (settingsChanged) {
                    Log.i(TAG, "Настройки изменены, применяем изменения");
                    
                    // Применяем настройки курсора
                    applyCursorSettings(cursorEnabled);
                    // Применяем дебаг-режим на карте (красный квадрат)
                    boolean debugEnabled = data.getBooleanExtra("debug_enabled", settingsManager.isDebugEnabled());
                    if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                        ((MapLibreMapImpl) mapController.getCurrentMapInterface()).setDebugMode(debugEnabled);
                    }
                    
                    // Применяем настройки морских знаков
                    boolean seamarksEnabled = data.getBooleanExtra("seamarks_enabled", settingsManager.isSeamarksEnabled());
                    if (mapController.getCurrentMapInterface() instanceof MapLibreMapImpl) {
                        ((MapLibreMapImpl) mapController.getCurrentMapInterface()).updateAdditionalLayers();
                    }
                    
                    if (needsRestart) {
                        Log.i(TAG, "Требуется перезапуск сервисов");
                        restartServices();
                    } else {
                        Log.i(TAG, "Применяем настройки без перезапуска");
                        applySettings();
                    }
                    refreshGpsSourceButtonIcon();

                    Toast.makeText(this, "Настройки применены", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
    
    // Меню
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        if (menuBinder != null) {
            menuBinder.onCreateOptionsMenu(menu);
        }
        return true;
    }
    
    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (menuBinder != null) {
            return menuBinder.onPrepareOptionsMenu(menu);
        }
        return true;
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (menuBinder != null && menuBinder.onOptionsItemSelected(item)) {
            return true;
        }
        int id = item.getItemId();
        if (id == R.id.menu_gps) {
            toggleGPS();
            return true;
        } else if (id == R.id.menu_udp) {
            toggleUDP();
            return true;
        } else if (id == R.id.menu_clear_ais) {
            clearAIS();
            return true;
        } else if (id == R.id.menu_path_tracking) {
            togglePathTracking();
            return true;
        } else if (id == R.id.menu_service_test) {
            testForegroundService();
            return true;
        } else if (id == R.id.menu_keep_screen_on) {
            toggleKeepScreenOn();
            return true;
        } else if (id == R.id.menu_seamarks) {
            toggleSeamarks();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    
    
    /**
     * Восстанавливает обработчики кликов для маркеров
     */
    private void restoreMarkerClickListeners() {
        Log.i(TAG, "Восстанавливаем обработчики кликов для маркеров");
        if (mapController.getCurrentMapInterface() instanceof com.grigowashere.aismap.maps.YandexMapImpl) {
            com.grigowashere.aismap.maps.YandexMapImpl yandexMap = (com.grigowashere.aismap.maps.YandexMapImpl) mapController.getCurrentMapInterface();
            yandexMap.refreshMarkerClickListeners();
            Log.i(TAG, "Обработчики кликов восстановлены");
        }
    }
    
    /**
     * Тестирует работу кликов по маркерам
     */
    private void testMarkerClicks() {
        Log.i(TAG, "Тестируем работу кликов по маркерам");
        Toast.makeText(this, "Тестируем клики по маркерам", Toast.LENGTH_SHORT).show();
        
        // Восстанавливаем обработчики кликов
        restoreMarkerClickListeners();
        
        // Проверяем, что маркеры существуют
        Vessel ownVessel = appCoordinator.getOwnVessel();
        if (ownVessel != null && ownVessel.getLatitude() != 0 && ownVessel.getLongitude() != 0) {
            Log.i(TAG, "Наше судно найдено, координаты: " + ownVessel.getLatitude() + ", " + ownVessel.getLongitude());
            Toast.makeText(this, "Наше судно найдено, попробуйте кликнуть по маркеру", Toast.LENGTH_LONG).show();
        } else {
            Log.w(TAG, "Наше судно не найдено или координаты равны 0");
            Toast.makeText(this, "Наше судно не найдено", Toast.LENGTH_SHORT).show();
        }
        
        // Проверяем AIS суда
        List<AISVessel> aisVessels = appCoordinator.getAISVessels();
        if (!aisVessels.isEmpty()) {
            Log.i(TAG, "Найдено AIS судов: " + aisVessels.size());
            Toast.makeText(this, "Найдено AIS судов: " + aisVessels.size(), Toast.LENGTH_SHORT).show();
        } else {
            Log.w(TAG, "AIS суда не найдены");
            Toast.makeText(this, "AIS суда не найдены", Toast.LENGTH_SHORT).show();
        }
    }
    
    /**
     * Инициализирует Яндекс.Карты
     */
    private void initializeYandexMaps() {
        if (!isYandexMapsInitialized) {
            try {
                // Инициализация Яндекс.Карт
                com.yandex.mapkit.MapKitFactory.setApiKey("9ae1917c-2049-4927-9d1e-29dd0d3e8ebc");
                com.yandex.mapkit.MapKitFactory.initialize(this);
                isYandexMapsInitialized = true;
                
                // Устанавливаем флаг в MapController
                MapController.setYandexMapsInitialized(true);
                
                Log.i(TAG, "Яндекс.Карты успешно инициализированы");
            } catch (Exception e) {
                Log.e(TAG, "Ошибка инициализации Яндекс.Карт: " + e.getMessage(), e);
            }
        }
    }
    
    /**
     * Применяет настройки к контроллерам
     */
    private void applySettings() {
        if (settingsManager == null || appCoordinator == null) {
            Log.w(TAG, "SettingsManager или AppCoordinator не инициализированы");
            return;
        }
        
        try {
            // Применяем настройки через координатор
            appCoordinator.applySettings();
            
            Log.i(TAG, "Настройки применены успешно");
        } catch (Exception e) {
            Log.e(TAG, "Ошибка при применении настроек: " + e.getMessage(), e);
            Toast.makeText(this, "Ошибка при применении настроек", Toast.LENGTH_SHORT).show();
        }
    }
    
    /**
     * Запускает форграунд сервис
     */
    private void startForegroundService() {
        try {
            // Проверяем разрешения для Android 13+
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
                if (androidx.core.content.ContextCompat.checkSelfPermission(this, android.Manifest.permission.POST_NOTIFICATIONS) 
                    != android.content.pm.PackageManager.PERMISSION_GRANTED) {
                    android.util.Log.w(TAG, "Запрашиваем разрешение на уведомления для Android 13+");
                    androidx.core.app.ActivityCompat.requestPermissions(this, 
                        new String[]{android.Manifest.permission.POST_NOTIFICATIONS}, 
                        NOTIFICATION_PERMISSION_REQUEST_CODE);
                    return; // Ждем разрешения
                }
            }
            
            android.content.Intent svc = new android.content.Intent(this, com.grigowashere.aismap.services.AISForegroundService.class);
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                startForegroundService(svc);
                android.util.Log.i(TAG, "Форграунд сервис запущен через startForegroundService()");
            } else {
                startService(svc);
                android.util.Log.i(TAG, "Форграунд сервис запущен через startService()");
            }
        } catch (Exception e) {
            android.util.Log.e(TAG, "Не удалось запустить форграунд сервис: " + e.getMessage(), e);
        }
    }
    
    /**
     * Останавливает форграунд сервис
     */
    private void stopForegroundService() {
        try {
            android.content.Intent svc = new android.content.Intent(this, com.grigowashere.aismap.services.AISForegroundService.class);
            stopService(svc);
            android.util.Log.i(TAG, "Форграунд сервис остановлен");
        } catch (Exception e) {
            android.util.Log.e(TAG, "Ошибка при остановке форграунд сервиса: " + e.getMessage(), e);
        }
    }
    
    /**
     * Тестирует работу форграунд сервиса
     */
    private void testForegroundService() {
        android.util.Log.i(TAG, "=== ТЕСТ ФОРГРАУНД СЕРВИСА ===");
        
        // Проверяем разрешения на уведомления
        boolean hasNotificationPermission = true;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
            hasNotificationPermission = androidx.core.content.ContextCompat.checkSelfPermission(this, 
                android.Manifest.permission.POST_NOTIFICATIONS) == android.content.pm.PackageManager.PERMISSION_GRANTED;
        }
        android.util.Log.i(TAG, "Разрешение на уведомления: " + hasNotificationPermission);
        
        // Проверяем статус сервиса
        boolean isServiceRunning = isServiceRunning();
        android.util.Log.i(TAG, "Сервис запущен: " + isServiceRunning);
        
        if (isServiceRunning) {
            android.util.Log.i(TAG, "Останавливаем сервис...");
            stopForegroundService();
            Toast.makeText(this, "Сервис остановлен", Toast.LENGTH_SHORT).show();
        } else {
            android.util.Log.i(TAG, "Запускаем сервис...");
            startForegroundService();
            Toast.makeText(this, "Сервис запущен", Toast.LENGTH_SHORT).show();
        }
        
        android.util.Log.i(TAG, "=== КОНЕЦ ТЕСТА ===");
    }
    
    /**
     * Проверяет, запущен ли сервис
     */
    private boolean isServiceRunning() {
        android.app.ActivityManager manager = (android.app.ActivityManager) getSystemService(android.content.Context.ACTIVITY_SERVICE);
        for (android.app.ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (com.grigowashere.aismap.services.AISForegroundService.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Перезапускает сервисы с новыми настройками
     */
    private void restartServices() {
        if (appCoordinator == null) {
            Log.w(TAG, "AppCoordinator не инициализирован");
            return;
        }
        
        try {
            Log.i(TAG, "Перезапускаем сервисы...");
            
            // Останавливаем все слушатели
            appCoordinator.stopAllControllers();
            
            // Применяем новые настройки
            applySettings();
            
            // Перезапускаем UDP слушатель с новым портом, если нужно
            if (settingsManager.shouldRestartUDP(appCoordinator.getUDPPort(), appCoordinator.isUDPEnabled())) {
                appCoordinator.restartUDPListener();
            }
            
            // Запускаем слушатели с новыми настройками
            appCoordinator.startAllControllers();
            
            Log.i(TAG, "Сервисы успешно перезапущены");
            Log.i(TAG, "Статус настроек: " + appCoordinator.getSettingsStatus());
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка при перезапуске сервисов: " + e.getMessage(), e);
            Toast.makeText(this, "Ошибка при перезапуске сервисов", Toast.LENGTH_SHORT).show();
        }
    }
    
    /**
     * Инициализирует курсор согласно настройкам
     */
    private void initializeCursor() {
        if (mapController.getCurrentMapInterface() == null || settingsManager == null) return;
        
        boolean cursorEnabled = settingsManager.isCursorEnabled();
        if (cursorEnabled) {
            mapController.getCurrentMapInterface().showCursor();
            // Обновляем координаты курсора с центра карты
            mapController.getCurrentMapInterface().updateCursorFromMapCenter();
        } else {
            mapController.getCurrentMapInterface().hideCursor();
        }
        
        Log.i(TAG, "Курсор инициализирован: " + (cursorEnabled ? "включен" : "выключен"));
    }
    
    /**
     * Применяет настройки курсора
     */
    private void applyCursorSettings(boolean cursorEnabled) {
        if (mapController.getCurrentMapInterface() == null) return;
        
        if (cursorEnabled) {
            mapController.getCurrentMapInterface().showCursor();
            // Обновляем координаты курсора с центра карты
            mapController.getCurrentMapInterface().updateCursorFromMapCenter();
        } else {
            mapController.getCurrentMapInterface().hideCursor();
        }
        
        Log.i(TAG, "Настройки курсора применены: " + (cursorEnabled ? "включен" : "выключен"));
    }
    
}