package com.grigowashere.aismap.maps;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.Log;

import com.grigowashere.aismap.models.AISVessel;
import com.grigowashere.aismap.models.AISNavigationAid;
import com.grigowashere.aismap.models.Vessel;
import com.grigowashere.aismap.utils.SettingsManager;
import com.grigowashere.aismap.utils.GeoUtils;
import com.grigowashere.aismap.controllers.VesselPathController;
import com.grigowashere.aismap.controllers.AppCoordinator;
import com.grigowashere.aismap.view.CursorOverlay;
import com.grigowashere.aismap.R;
import android.view.ViewGroup;
import android.view.View;
import android.view.ViewParent;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.List;
import org.maplibre.android.maps.MapView;
import org.maplibre.android.maps.MapLibreMap;
import org.maplibre.android.maps.Style;
import org.maplibre.android.storage.FileSource;
import org.maplibre.android.geometry.LatLng;
import org.maplibre.android.style.expressions.Expression;
import org.maplibre.android.style.layers.PropertyFactory;
import org.maplibre.android.style.layers.SymbolLayer;
import org.maplibre.android.style.sources.GeoJsonSource;
import org.maplibre.android.style.sources.RasterSource;
import org.maplibre.android.style.layers.RasterLayer;

import java.util.HashMap;
import java.util.Map;

/**
 * Реализация MapInterface на базе MapLibre GL
 */
public class MapLibreMapImpl implements MapInterface {

    private static final String TAG = "MapLibreMapImpl";

    private static final String SOURCE_VESSELS = "vessels_source";
    private static final String LAYER_VESSELS = "vessels_layer";
    private static final String LAYER_VESSELS_STALE = "vessels_layer_stale";
    private static final String LAYER_STATUS_OVERLAY = "vessels_layer_status";
    private static final String SOURCE_OWN_PATH = "own_path_source";
    private static final String LAYER_OWN_PATH = "own_path_layer";
    private static final String SOURCE_OWN_PRED = "own_prediction_source";
    private static final String LAYER_OWN_PRED = "own_prediction_layer";
    private static final String SOURCE_AIS_PATHS = "ais_paths_source";
    private static final String LAYER_AIS_PATHS = "ais_paths_layer";
    private static final String SOURCE_AIS_PREDICTIONS = "ais_predictions_source";
    private static final String LAYER_AIS_PREDICTIONS = "ais_predictions_layer";
    private static final String SOURCE_SEAMARKS = "seamarks_source";
    private static final String LAYER_SEAMARKS = "seamarks_layer";
    private static final String SOURCE_NAVIGATION_AIDS = "navigation_aids_source";
    private static final String LAYER_NAVIGATION_AIDS = "navigation_aids_layer";
    private static final String IMAGE_VESSEL_OWN = "ownship";
    private static final String IMAGE_VESSEL_A = "vessel_icon_a";
    private static final String IMAGE_VESSEL_B = "vessel_icon_b";
    // Имиджи, сопоставленные с ресурсами drawable (target_*.xml/png)
    // Базовые имена без класса; для выбора добавляем префикс target_a_ / target_b_
    private static final String TYPE_CARGO = "cargo";
    private static final String TYPE_TANKER = "tanker";
    private static final String TYPE_FISHING = "fishing";
    private static final String TYPE_PASSENGER = "passenger";
    private static final String TYPE_PLEASURE = "pleasure";
    private static final String TYPE_TUG = "tug";
    private static final String TYPE_NAVY = "navy";
    private static final String TYPE_OTHER = "other";
    private static final String IMAGE_VESSEL_LOSING = "vessel_icon_losing";
    // Navigation Aid icons - полный набор для всех типов буйков
    private static final String IMAGE_NAVIGATION_AID = "navigation_aid";
    
    // Кардинальные буи (North, East, South, West)
    private static final String IMAGE_BUOY_CARDINAL_N = "buoy_cardinal_n";
    private static final String IMAGE_BUOY_CARDINAL_E = "buoy_cardinal_e";
    private static final String IMAGE_BUOY_CARDINAL_S = "buoy_cardinal_s";
    private static final String IMAGE_BUOY_CARDINAL_W = "buoy_cardinal_w";
    private static final String IMAGE_BUOY_CARDINAL = "buoy_cardinal"; // Общий fallback
    
    // Латеральные буи (Port/Starboard)
    private static final String IMAGE_BUOY_PORT = "buoy_port";
    private static final String IMAGE_BUOY_STARBOARD = "buoy_starboard";
    
    // Предпочтительные каналы
    private static final String IMAGE_BUOY_PREFERRED_PORT = "buoy_preferred_port";
    private static final String IMAGE_BUOY_PREFERRED_STARBOARD = "buoy_preferred_starboard";
    
    // Маяки и огни
    private static final String IMAGE_LIGHT = "light";
    private static final String IMAGE_LIGHT_SECTOR = "light_sector";
    private static final String IMAGE_LIGHT_LEADING_FRONT = "light_leading_front";
    private static final String IMAGE_LIGHT_LEADING_REAR = "light_leading_rear";
    
    // Буи и знаки
    private static final String IMAGE_BEACON = "beacon";
    private static final String IMAGE_BEACON_ISOLATED_DANGER = "beacon_isolated_danger";
    private static final String IMAGE_BEACON_SAFE_WATER = "beacon_safe_water";
    private static final String IMAGE_BEACON_SPECIAL = "beacon_special";
    
    // Платформы и плавучие маяки
    private static final String IMAGE_BASE_STATION = "base_station";
    private static final String IMAGE_LIGHT_VESSEL = "light_vessel";
    private static final String IMAGE_PLATFORM = "platform";
    
    // RACON и специальные знаки
    private static final String IMAGE_RACON = "racon";
    private static final String IMAGE_REFERENCE_POINT = "reference_point";
    // Status overlay drawable names present in res/drawable
    private static final String STATUS_UNDER_WAY_ENGINE = "engine";
    private static final String STATUS_AT_ANCHOR = "achor"; // anchor icon filename
    private static final String STATUS_MOORED = "moored"; // no dedicated icon yet
    private static final String STATUS_FISHING = "fishing";
    private static final String STATUS_SAILING = "sail";
    private static final boolean PATH_FEATURES_ENABLED = true;

    private final Context context;
    private final MapView mapView;
    private MapLibreMap maplibreMap;
    private Style style;
    private final SettingsManager settingsManager;
    private VesselPathController pathController;
    // AppController удален; используем AppCoordinator для доступа к данным
    private AppCoordinator appCoordinator; // Новый координатор для доступа к AIS судам
    private CursorOverlay cursorOverlay;
    private Vessel ownVessel;
    
    // Отладка
    private boolean debugMode = true; // Включаем отладку по умолчанию
    private android.graphics.RectF lastSearchRect = null;
    private final android.os.Handler uiHandler = new android.os.Handler(android.os.Looper.getMainLooper());
    private final android.os.Handler staleHandler = new android.os.Handler(android.os.Looper.getMainLooper());
    
    // Throttling для обновлений карты
    private final android.os.Handler mapUpdateHandler = new android.os.Handler(android.os.Looper.getMainLooper());
    private Runnable mapUpdateRunnable;
    private boolean mapUpdatePending = false;
    private Vessel pendingVesselUpdate = null;
    private static final long MAP_UPDATE_DELAY = 500; // 500ms throttling
    
    // Throttling для refreshGeoJson
    private final android.os.Handler geoJsonUpdateHandler = new android.os.Handler(android.os.Looper.getMainLooper());
    private Runnable geoJsonUpdateRunnable;
    private boolean geoJsonUpdatePending = false;
    private static final long GEOJSON_UPDATE_DELAY = 1000; // 1сек throttling для тяжелой операции
    private final Runnable staleRunnable = new Runnable() {
        @Override
        public void run() {
            try {
                boolean changed = false;
                java.util.List<String> toRemove = new java.util.ArrayList<>();
                int warnMin = settingsManager.getDataStaleWarningMinutes();
                int removeMin = settingsManager.getDataStaleRemoveMinutes();

                for (Map.Entry<String, AISVessel> e : idToAisVessel.entrySet()) {
                    String mmsi = e.getKey();
                    AISVessel v = e.getValue();
                    if (v == null) continue;
                    if (v.shouldBeRemoved(removeMin)) {
                        toRemove.add(mmsi);
                        continue;
                    }
                    boolean stale = v.isDataStale(warnMin);
                    JSONObject f = idToFeature.get(mmsi);
                    if (f != null) {
                        try {
                            JSONObject props = f.getJSONObject("properties");
                            boolean prev = props.optBoolean("stale", false);
                            if (prev != stale) {
                                props.put("stale", stale);
                                changed = true;
                            }
                        } catch (Exception ignore) {}
                    }
                }

                if (!toRemove.isEmpty()) {
                    // Удаляем суда без обновления UI
                    for (String m : toRemove) {
                        idToFeature.remove(m);
                        idToAisVessel.remove(m);
                        
                        // Очищаем путь AIS судна через AppCoordinator
                        if (appCoordinator != null) {
                            VesselPathController c = appCoordinator.getAISVesselPathController(m);
                            if (c != null) c.clearPath();
                        }
                        
                        // Очищаем путь и прогноз AIS судна
                        aisPathFeatures.remove(m);
                        aisPredictionFeatures.remove(m);
                    }
                    changed = true;
                }

                if (changed) {
                    // Переносим обновление GeoJSON на UI поток
                    uiHandler.post(() -> {
                        refreshGeoJson();
                        refreshAISPathsSource();
                        refreshAISPredictionsSource();
                    });
                }
                
                // Логируем память каждые 10 секунд
                logMemoryUsage();
            } finally {
                staleHandler.postDelayed(this, 10_000L);
            }
        }
    };

    // Хранилище текущих GeoJSON фич (ключ: mmsi или "own_vessel")
    private final Map<String, JSONObject> idToFeature = new HashMap<>();
    // Хранилище последних модельных объектов для кликов
    private final Map<String, AISVessel> idToAisVessel = new HashMap<>();
    private final Map<String, AISNavigationAid> idToNavigationAid = new HashMap<>();
    private Vessel lastOwnVessel;
    // Буфер координат пути собственного судна
    private final JSONArray ownPathCoords = new JSONArray();
    // Буферы координат путей AIS судов больше не нужны - используем VesselPathController из AppCoordinator
    
    // Хранилище FeatureCollection для путей и прогнозов AIS судов
    private final Map<String, JSONObject> aisPathFeatures = new HashMap<>();
    private final Map<String, JSONObject> aisPredictionFeatures = new HashMap<>();

    private MarkerClickListener markerClickListener;

    // Pending центрирование до готовности карты/стиля
    private Double pendingCenterLat = null;
    private Double pendingCenterLon = null;

    public MapLibreMapImpl(Context context, MapView mapView) {
        this.context = context;
        this.mapView = mapView;
        this.settingsManager = new SettingsManager(context);
        // VesselPathController будет установлен через setVesselPathController()
        this.pathController = null;
        this.cursorOverlay = new CursorOverlay(context);
        // Инициализируем флаг отладки из настроек
        this.debugMode = settingsManager.isDebugEnabled();
        
        // Добавляем overlay курсора НАД картой: в родителя MapView, чтобы он был поверх GL слоя
        ViewParent mapParent = mapView.getParent();
        if (mapParent instanceof ViewGroup) {
            ViewGroup parent = (ViewGroup) mapParent;
            // Удаляем все прежние cursor_overlay из родителя (на случай повторной инициализации)
            for (int i = parent.getChildCount() - 1; i >= 0; i--) {
                View child = parent.getChildAt(i);
                Object tag = child.getTag();
                if (tag != null && "cursor_overlay".equals(tag)) {
                    parent.removeViewAt(i);
                }
            }

            View v = cursorOverlay.getView();
            if (v.getParent() != parent) {
                if (v.getParent() instanceof ViewGroup) {
                    ((ViewGroup) v.getParent()).removeView(v);
                }
                ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                );
                // Вставляем поверх MapView
                int mapIndex = parent.indexOfChild(mapView);
                int insertIndex = Math.min(mapIndex + 1, parent.getChildCount());
                parent.addView(v, insertIndex, lp);
            }
            // Поднимаем на передний план
            v.bringToFront();
            v.setTranslationZ(2f);
        }
        
        // Инициализируем throttling для карты
        setupMapThrottling();
    }
    
    /**
     * Устанавливает VesselPathController для использования
     */
    public void setVesselPathController(VesselPathController pathController) {
        this.pathController = pathController;
        Log.i(TAG, "VesselPathController установлен в MapLibreMapImpl");
    }
    
    // setAppController удален
    
    /**
     * Устанавливает AppCoordinator для доступа к AIS судам
     */
    public void setAppCoordinator(AppCoordinator appCoordinator) {
        this.appCoordinator = appCoordinator;
        Log.i(TAG, "AppCoordinator установлен в MapLibreMapImpl");
    }
    
    /**
     * Настраивает throttling для обновлений карты
     */
    private void setupMapThrottling() {
        mapUpdateRunnable = () -> {
            mapUpdatePending = false;
            updateMapBatched(pendingVesselUpdate);
            pendingVesselUpdate = null;
        };
        
        // Инициализируем throttling для refreshGeoJson
        geoJsonUpdateRunnable = () -> {
            geoJsonUpdatePending = false;
            refreshGeoJsonInternal();
        };
    }

    @Override
    public void initialize() {
        try {
            mapView.getMapAsync(map -> {
                maplibreMap = map;
                // Отключаем встроенный компас MapLibre (он появлялся в углу
                // при ненулевом bearing и дублировал наш компас), а также
                // стандартные UI-элементы, которые нам не нужны.
                try {
                    if (maplibreMap.getUiSettings() != null) {
                        maplibreMap.getUiSettings().setCompassEnabled(false);
                        maplibreMap.getUiSettings().setAttributionEnabled(false);
                        maplibreMap.getUiSettings().setLogoEnabled(false);
                    }
                } catch (Exception e) {
                    Log.w(TAG, "Не удалось настроить UI MapLibre: " + e.getMessage());
                }
                maplibreMap.setStyle("https://basemaps.cartocdn.com/gl/positron-gl-style/style.json", loadedStyle -> {
                    style = loadedStyle;
                    ensureSourcesAndLayers();
                    // УДАЛЯЕМ ИНИЦИАЛИЗАЦИЮ СТАРЫХ ОБЩИХ ИСТОЧНИКОВ - теперь используем индивидуальные
                    // if (PATH_FEATURES_ENABLED) {
                    //     // Инициализируем пустые слои пути/предсказания
                    //     updateOwnPathSource();
                    //     updateOwnPredictionSource();
                    // }
                    // Обновим источник текущими фичами, чтобы метки появились сразу после загрузки стиля
                    refreshGeoJson();
                    setupClickListener();
                    setupMapMovementListener();
                    
                    // Устанавливаем размеры экрана для курсора после инициализации карты
                    if (cursorOverlay != null && mapView != null) {
                        mapView.post(() -> {
                            int width = mapView.getWidth();
                            int height = mapView.getHeight();
                            if (width > 0 && height > 0) {
                                cursorOverlay.setScreenDimensions(width, height);
                                Log.d(TAG, String.format("Установлены размеры экрана для курсора: %dx%d", width, height));
                            }
                        });
                    }
                    
                    staleHandler.removeCallbacks(staleRunnable);
                    staleHandler.postDelayed(staleRunnable, 5_000L);

                    // Если было отложенное центрирование — применим его сразу после загрузки стиля
                    if (pendingCenterLat != null && pendingCenterLon != null) {
                        final double lat = pendingCenterLat;
                        final double lon = pendingCenterLon;
                        pendingCenterLat = null;
                        pendingCenterLon = null;
                        uiHandler.post(() -> centerOnPosition(lat, lon));
                    }
                });
            });
        } catch (Exception e) {
            Log.e(TAG, "Ошибка инициализации MapLibre: " + e.getMessage(), e);
        }
    }

    @Override
    public void cleanup() {
        try {
            if (maplibreMap != null) {
                maplibreMap.removeOnMapClickListener(onMapClickListener);
            }
            // Удаляем overlay курсора из родителя, чтобы не накапливался между активити
            if (mapView != null) {
                ViewParent mapParent = mapView.getParent();
                if (mapParent instanceof ViewGroup) {
                    ViewGroup parent = (ViewGroup) mapParent;
                    for (int i = parent.getChildCount() - 1; i >= 0; i--) {
                        View child = parent.getChildAt(i);
                        Object tag = child.getTag();
                        if (tag != null && "cursor_overlay".equals(tag)) {
                            parent.removeViewAt(i);
                        }
                    }
                }
                mapView.onStop();
            }
            staleHandler.removeCallbacks(staleRunnable);
            
            // Останавливаем throttling handlers
            if (mapUpdateHandler != null && mapUpdateRunnable != null) {
                mapUpdateHandler.removeCallbacks(mapUpdateRunnable);
            }
            if (geoJsonUpdateHandler != null && geoJsonUpdateRunnable != null) {
                geoJsonUpdateHandler.removeCallbacks(geoJsonUpdateRunnable);
            }
        } catch (Exception ignored) {}
    }

    @Override
    public void addOwnVesselMarker(Vessel vessel) {
        this.ownVessel = vessel;
        if (cursorOverlay != null) {
            cursorOverlay.setOwnVessel(vessel);
        }
        updateOwnVesselPosition(vessel);
    }

    @Override
    public void updateOwnVesselPosition(Vessel vessel) {
        if (vessel == null) return;
        
        this.ownVessel = vessel;
        if (cursorOverlay != null) {
            cursorOverlay.setOwnVessel(vessel);
        }
        
        // Сохраняем последнее судно для отображения
        lastOwnVessel = vessel;
        
        // Обновляем данные судна сразу (не блокирующее)
        try {
            JSONObject feature = buildFeature(
                "own_vessel",
                vessel.getLongitude(),
                vessel.getLatitude(),
                vessel.getCourse(),
                true
            );
            idToFeature.put("own_vessel", feature);
        } catch (Exception e) {
            Log.e(TAG, "Ошибка создания feature для собственного судна: " + e.getMessage(), e);
        }
        
        // Throttled обновление карты
        updateMapThrottled(vessel);
    }
    
    /**
     * Безопасное throttled обновление карты
     */
    private void updateMapThrottled(Vessel vessel) {
        if (!mapUpdatePending) {
            mapUpdatePending = true;
            pendingVesselUpdate = vessel;
            mapUpdateHandler.removeCallbacks(mapUpdateRunnable);
            mapUpdateHandler.postDelayed(mapUpdateRunnable, MAP_UPDATE_DELAY);
        }
    }
    
    /**
     * Батчевое обновление карты (все операции в одном UI вызове)
     */
    private void updateMapBatched(Vessel vessel) {
        if (vessel == null) return;
        
        try {
            // Убираем лишние логи
            // Log.d(TAG, "updateMapBatched: vessel=" + vessel.getLatitude() + "," + vessel.getLongitude() + 
            //       ", speed=" + vessel.getSpeed() + ", course=" + vessel.getCourse());
            
            uiHandler.post(() -> {
                try {
                    if (PATH_FEATURES_ENABLED) {
                        // 1. Обновляем путь судна (если есть точки)
                        if (pathController != null && pathController.getPathPointsCount() >= 2) {
                            JSONArray pathCoords = pathController.getPathCoordinatesWithSpeed();
                            updateOwnVesselPathSource("own_vessel", pathCoords);
                        }
                        
                        // 2. Обновляем прогноз
                        updateOwnVesselPredictionSource("own_vessel", vessel);
                    }
                    
                    // 3. Обновляем GeoJSON (маркеры судов)
                    refreshGeoJson();
                    
                    Log.d(TAG, "Карта обновлена батчом");
                } catch (Exception e) {
                    Log.e(TAG, "Ошибка в updateMapBatched: " + e.getMessage(), e);
                }
            });
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка при подготовке updateMapBatched: " + e.getMessage(), e);
        }
    }

    @Override
    public void addAISVesselMarker(AISVessel vessel) {
        updateAISVesselPosition(vessel);
    }

    @Override
    public void updateAISVesselPosition(AISVessel vessel) {
        if (vessel == null || vessel.getMmsi() == null) return;
        
        // Проверяем валидность координат
        if (!GeoUtils.isValidCoordinates(vessel.getLatitude(), vessel.getLongitude())) {
            Log.d(TAG, "updateAISVesselPosition: AIS vessel " + vessel.getMmsi() + 
                  " has invalid coordinates " + vessel.getLatitude() + "," + vessel.getLongitude() + 
                  " - skipping marker and path update");
            return;
        }
        
        try {
            Log.d(TAG, "updateAISVesselPosition: AIS vessel " + vessel.getMmsi() + 
                  " at " + vessel.getLatitude() + "," + vessel.getLongitude() + 
                  ", speed=" + vessel.getSpeed() + ", course=" + vessel.getCourse());
            
            // Удаление по таймауту
            if (vessel.shouldBeRemoved(settingsManager.getDataStaleRemoveMinutes())) {
                removeAISVesselMarker(vessel.getMmsi());
                return;
            }
            idToAisVessel.put(vessel.getMmsi(), vessel);
            
            // Добавляем трассировку пути и предсказание для AIS судна
            if (PATH_FEATURES_ENABLED) {
                Log.d(TAG, "PATH_FEATURES_ENABLED=true, updating AIS path and prediction for " + vessel.getMmsi());
                // Переносим обновление AIS пути на UI поток (используем VesselPathController из AppCoordinator)
                uiHandler.post(() -> updateAISPathSource(vessel.getMmsi()));
                // Переносим обновление AIS прогноза на UI поток
                uiHandler.post(() -> updateAISVesselPredictionSource(vessel.getMmsi(), vessel));
            } else {
                Log.d(TAG, "PATH_FEATURES_ENABLED=false, skipping AIS path and prediction update for " + vessel.getMmsi());
            }
            
            JSONObject feature = buildFeature(
                vessel.getMmsi(),
                vessel.getLongitude(),
                vessel.getLatitude(),
                getDisplayCourse(vessel),
                false
            );
            try {
                boolean stale = vessel.isDataStale(settingsManager.getDataStaleWarningMinutes());
                JSONObject props = feature.getJSONObject("properties");
                // Выберем корректное имя ресурса target_{a|b}_{type}
                String iconName = pickIconNameFor(vessel);
                props.put("icon", iconName);
                props.put("stale", stale);
                // Проставим статусную иконку, если статус поддержан
                String status = vessel.getNavigationalStatus();
                String statusIcon = mapStatusToIcon(status);
                if (statusIcon != null) {
                    props.put("status_icon", statusIcon);
                } else {
                    props.remove("status_icon");
                }
            } catch (Exception ignore) {}
            idToFeature.put(vessel.getMmsi(), feature);
            // Переносим обновление GeoJSON на UI поток
            uiHandler.post(() -> refreshGeoJson());
        } catch (Exception e) {
            Log.e(TAG, "updateAISVesselPosition: " + e.getMessage(), e);
        }
    }

    @Override
    public void updateAISVesselPositions(List<AISVessel> vessels) {
        if (vessels == null || vessels.isEmpty()) return;

        try {
            int updated = 0;
            int removed = 0;
            int skipped = 0;

            for (AISVessel vessel : vessels) {
                if (vessel == null || vessel.getMmsi() == null) {
                    skipped++;
                    continue;
                }
                if (!GeoUtils.isValidCoordinates(vessel.getLatitude(), vessel.getLongitude())) {
                    skipped++;
                    continue;
                }
                if (vessel.shouldBeRemoved(settingsManager.getDataStaleRemoveMinutes())) {
                    idToFeature.remove(vessel.getMmsi());
                    idToAisVessel.remove(vessel.getMmsi());
                    aisPathFeatures.remove(vessel.getMmsi());
                    aisPredictionFeatures.remove(vessel.getMmsi());
                    removed++;
                    continue;
                }

                idToAisVessel.put(vessel.getMmsi(), vessel);
                JSONObject feature = buildFeature(
                    vessel.getMmsi(),
                    vessel.getLongitude(),
                    vessel.getLatitude(),
                    getDisplayCourse(vessel),
                    false
                );
                try {
                    boolean stale = vessel.isDataStale(settingsManager.getDataStaleWarningMinutes());
                    JSONObject props = feature.getJSONObject("properties");
                    props.put("icon", pickIconNameFor(vessel));
                    props.put("stale", stale);
                    String statusIcon = mapStatusToIcon(vessel.getNavigationalStatus());
                    if (statusIcon != null) {
                        props.put("status_icon", statusIcon);
                    } else {
                        props.remove("status_icon");
                    }
                } catch (Exception ignore) {}

                idToFeature.put(vessel.getMmsi(), feature);
                updated++;
            }

            refreshGeoJson();
            Log.d(TAG, "AIS batch update: updated=" + updated +
                    ", removed=" + removed + ", skipped=" + skipped);
        } catch (Exception e) {
            Log.e(TAG, "updateAISVesselPositions: " + e.getMessage(), e);
        }
    }

    @Override
    public void removeAISVesselMarker(String mmsi) {
        if (mmsi == null) return;
        idToFeature.remove(mmsi);
        idToAisVessel.remove(mmsi);
        
        // Очищаем путь AIS судна через AppCoordinator
        if (appCoordinator != null) {
            try {
                VesselPathController c = appCoordinator.getAISVesselPathController(mmsi);
                if (c != null) c.clearPath();
            } catch (Exception ignore) {}
        }
        
        // Очищаем путь и прогноз AIS судна на UI потоке
        uiHandler.post(() -> {
            removeAISVesselPath(mmsi);
            removeAISVesselPrediction(mmsi);
        });
        
        // Переносим обновление GeoJSON на UI поток
        uiHandler.post(() -> refreshGeoJson());
    }

    @Override
    public void clearAISVesselMarkers() {
        // Удаляем все, кроме собственного судна
        idToFeature.entrySet().removeIf(e -> !"own_vessel".equals(e.getKey()));
        idToAisVessel.clear();
        
        // Очищаем все пути AIS судов через AppCoordinator
        if (appCoordinator != null) {
            try {
                java.util.List<AISVessel> list = appCoordinator.getAISVessels();
                if (list != null) {
                    for (AISVessel v : list) {
                        VesselPathController c = appCoordinator.getAISVesselPathController(v.getMmsi());
                        if (c != null) c.clearPath();
                    }
                }
            } catch (Exception ignore) {}
        }
        
        // Очищаем все пути и прогнозы AIS судов на UI потоке
        uiHandler.post(() -> {
            clearAllAISVesselPaths();
            clearAllAISVesselPredictions();
        });
        
        // Переносим обновление GeoJSON на UI поток
        uiHandler.post(() -> refreshGeoJson());
    }

    /**
     * Добавляет навигационный знак на карту
     */
    public void addNavigationAidMarker(AISNavigationAid navigationAid) {
        updateNavigationAidPosition(navigationAid);
    }

    /**
     * Обновляет позицию навигационного знака на карте
     */
    public void updateNavigationAidPosition(AISNavigationAid navigationAid) {
        if (!isStyleValid()) {
            Log.w(TAG, "Style not ready, skipping updateNavigationAidPosition");
            return;
        }

        try {
            String mmsi = navigationAid.getMmsi();
            Log.d(TAG, "updateNavigationAidPosition: Navigation aid " + mmsi + " at " + 
                navigationAid.getLatitude() + "," + navigationAid.getLongitude() + 
                ", type=" + navigationAid.getAidType() + ", name='" + navigationAid.getAidName() + "'");

            // Создаем GeoJSON фичу для навигационного знака
            JSONObject feature = buildNavigationAidFeature(navigationAid);
            idToFeature.put(mmsi, feature);
            idToNavigationAid.put(mmsi, navigationAid);

            // Обновляем источник данных
            refreshNavigationAidsSource();

            Log.d(TAG, "Navigation aid " + mmsi + " updated on map");
        } catch (Exception e) {
            Log.e(TAG, "Error updating navigation aid position: " + e.getMessage(), e);
        }
    }

    /**
     * Удаляет навигационный знак с карты
     */
    public void removeNavigationAidMarker(String mmsi) {
        if (!isStyleValid()) {
            Log.w(TAG, "Style not ready, skipping removeNavigationAidMarker");
            return;
        }

        try {
            // Удаляем из хранилищ
            idToFeature.remove(mmsi);
            idToNavigationAid.remove(mmsi);

            // Обновляем источник данных
            refreshNavigationAidsSource();

            Log.d(TAG, "Navigation aid " + mmsi + " removed from map");
        } catch (Exception e) {
            Log.e(TAG, "Error removing navigation aid marker: " + e.getMessage(), e);
        }
    }

    /**
     * Очищает все навигационные знаки с карты
     */
    public void clearNavigationAidMarkers() {
        if (!isStyleValid()) {
            Log.w(TAG, "Style not ready, skipping clearNavigationAidMarkers");
            return;
        }

        try {
            GeoJsonSource source = style.getSourceAs(SOURCE_NAVIGATION_AIDS);
            if (source != null) {
                source.setGeoJson(emptyFeatureCollection());
            }
            
            // Очищаем хранилища
            idToNavigationAid.clear();
            
            Log.d(TAG, "Navigation aid markers cleared");
        } catch (Exception e) {
            Log.e(TAG, "Error clearing navigation aid markers: " + e.getMessage(), e);
        }
    }

    @Override
    public void centerOnPosition(double latitude, double longitude) {
        if (maplibreMap == null || style == null) {
            // Сохраним pending, применим после загрузки стиля
            pendingCenterLat = latitude;
            pendingCenterLon = longitude;
            // И на всякий случай повторим позже
            try {
                if (uiHandler != null) {
                    uiHandler.postDelayed(() -> centerOnPosition(latitude, longitude), 300);
                }
            } catch (Exception ignore) {}
            return;
        }
        try {
            org.maplibre.android.camera.CameraPosition current = maplibreMap.getCameraPosition();
            float targetZoom = (float) current.zoom;
            
            // Если зум слишком маленький (меньше 5), используем стартовый зум из настроек
            if (targetZoom < 5.0f) {
                targetZoom = settingsManager.getStartZoomLevel();
                Log.i(TAG, "Принудительно устанавливаем стартовый зум: " + targetZoom);
            }
            
            maplibreMap.setCameraPosition(new org.maplibre.android.camera.CameraPosition.Builder()
                .target(new LatLng(latitude, longitude))
                .zoom(targetZoom)
                .tilt(current.tilt)
                .bearing(current.bearing)
                .build());
        } catch (Exception e) {
            Log.w(TAG, "centerOnPosition: MapView may be destroyed: " + e.getMessage());
        }
    }

    @Override
    public void setZoom(float zoom) {
        if (maplibreMap == null || mapView == null) return;
        try {
            org.maplibre.android.camera.CameraPosition current = maplibreMap.getCameraPosition();
            maplibreMap.setCameraPosition(new org.maplibre.android.camera.CameraPosition.Builder()
                .target(current.target)
                .zoom(zoom)
                .tilt(current.tilt)
                .bearing(current.bearing)
                .build());
        } catch (Exception e) {
            Log.w(TAG, "setZoom: MapView may be destroyed: " + e.getMessage());
        }
    }

    @Override
    public void setBearing(float bearing) {
        if (maplibreMap == null || mapView == null) return;
        try {
            org.maplibre.android.camera.CameraPosition current = maplibreMap.getCameraPosition();
            maplibreMap.setCameraPosition(new org.maplibre.android.camera.CameraPosition.Builder()
                .target(current.target)
                .zoom((float) current.zoom)
                .tilt(current.tilt)
                .bearing(bearing)
                .build());
        } catch (Exception e) {
            Log.w(TAG, "setBearing: MapView may be destroyed: " + e.getMessage());
        }
    }

    @Override
    public float getBearing() {
        if (maplibreMap == null) return 0f;
        try {
            return (float) maplibreMap.getCameraPosition().bearing;
        } catch (Exception e) {
            Log.w(TAG, "getBearing: MapView may be destroyed: " + e.getMessage());
            return 0f;
        }
    }

    @Override
    public float getZoom() {
        if (maplibreMap == null || mapView == null) return 0f;
        try {
            return (float) maplibreMap.getCameraPosition().zoom;
        } catch (Exception e) {
            Log.w(TAG, "getZoom: MapView may be destroyed: " + e.getMessage());
            return 0f;
        }
    }

    @Override
    public void addLayer(String layerId, Object layerData) {
        if (style == null || !isStyleValid()) {
            Log.w(TAG, "addLayer: стиль не готов, откладываем добавление слоя " + layerId);
            return;
        }
        
        try {
            if ("seamarks".equals(layerId)) {
                addSeamarksLayer();
            } else {
                Log.w(TAG, "addLayer: неизвестный тип слоя " + layerId);
            }
        } catch (Exception e) {
            Log.e(TAG, "addLayer: ошибка добавления слоя " + layerId, e);
        }
    }

    @Override
    public void removeLayer(String layerId) {
        if (style == null || !isStyleValid()) {
            Log.w(TAG, "removeLayer: стиль не готов, пропускаем удаление слоя " + layerId);
            return;
        }
        
        try {
            if ("seamarks".equals(layerId)) {
                removeSeamarksLayer();
            } else {
                Log.w(TAG, "removeLayer: неизвестный тип слоя " + layerId);
            }
        } catch (Exception e) {
            Log.e(TAG, "removeLayer: ошибка удаления слоя " + layerId, e);
        }
    }

    @Override
    public void setMarkerClickListener(MarkerClickListener listener) {
        this.markerClickListener = listener;
    }

    private void ensureSourcesAndLayers() {
        if (style == null) {
            Log.d(TAG, "ensureSourcesAndLayers: style is null");
            return;
        }
        Log.d(TAG, "ensureSourcesAndLayers: starting...");

        // Иконки судов: own, class A, class B
        try {
            if (style.getImage(IMAGE_VESSEL_OWN) == null) {
                Bitmap bmpOwn = getBitmapByName("ownship");
                if (bmpOwn == null) bmpOwn = BitmapFactory.decodeResource(context.getResources(), android.R.drawable.ic_menu_compass);
                // Добавляем без SDF для сохранения цвета
                style.addImage(IMAGE_VESSEL_OWN, bmpOwn, false);
            }
            // Предзагрузка цветных иконок из ресурсов target_a_* и target_b_*
            preloadClassTypeIcons("a");
            preloadClassTypeIcons("b");
            if (style.getImage(IMAGE_VESSEL_LOSING) == null) {
                Bitmap bmpLosing = getBitmapByName("losingtarget");
                if (bmpLosing != null) {
                    style.addImage(IMAGE_VESSEL_LOSING, bmpLosing);
                }
            }
            // Предзагрузка статусных иконок
            preloadImageIfMissing(STATUS_UNDER_WAY_ENGINE);
            preloadImageIfMissing(STATUS_AT_ANCHOR);
            if (STATUS_MOORED != null) preloadImageIfMissing(STATUS_MOORED);
            preloadImageIfMissing(STATUS_FISHING);
            preloadImageIfMissing(STATUS_SAILING);
            // Иконка для Aid-to-Navigation
            preloadImageIfMissing("green_buey");
            // Иконка для Base Station
            preloadImageIfMissing("base_station");
        } catch (Exception e) {
            Log.w(TAG, "Не удалось добавить иконки: " + e.getMessage());
        }

        // Источник GeoJSON для судов
        if (style.getSource(SOURCE_VESSELS) == null) {
            style.addSource(new GeoJsonSource(SOURCE_VESSELS, emptyFeatureCollection()));
        }

        // Источник GeoJSON для навигационных знаков
        if (style.getSource(SOURCE_NAVIGATION_AIDS) == null) {
            style.addSource(new GeoJsonSource(SOURCE_NAVIGATION_AIDS, emptyFeatureCollection()));
        }

        // Отладочные линии удалены

        // Слой символов (основные иконки)
        if (style.getLayer(LAYER_VESSELS) == null) {
            SymbolLayer layer = new SymbolLayer(LAYER_VESSELS, SOURCE_VESSELS)
                .withProperties(
                    // Берём имя иконки из свойства feature.properties.icon (target_a_*/target_b_*), c дефолтом
                    PropertyFactory.iconImage(
                        Expression.coalesce(
                            Expression.get("icon"),
                            Expression.literal("target_b_other")
                        )
                    ),
                    PropertyFactory.iconAnchor(org.maplibre.android.style.layers.Property.ICON_ANCHOR_CENTER),
                    PropertyFactory.iconRotationAlignment(org.maplibre.android.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP),
                    PropertyFactory.iconRotate(Expression.get("course")),
                    // Цвет задаём через предгенерированные картинки, iconColor не используем
                    PropertyFactory.iconAllowOverlap(true),
                    PropertyFactory.iconIgnorePlacement(true),
                    PropertyFactory.iconSize(
                        Expression.interpolate(
                            Expression.linear(),
                            Expression.zoom(),
                            Expression.stop(5, 0.085f),
                            Expression.stop(8, 0.115f),
                            Expression.stop(12, 0.165f),
                            Expression.stop(15, 0.235f),
                            Expression.stop(17, 0.300f)
                        )
                    )
                );
            
            
            style.addLayer(layer);
        }

        // Слой навигационных знаков
        if (style.getLayer(LAYER_NAVIGATION_AIDS) == null) {
            SymbolLayer navigationAidLayer = new SymbolLayer(LAYER_NAVIGATION_AIDS, SOURCE_NAVIGATION_AIDS)
                .withProperties(
                    PropertyFactory.iconImage(
                        Expression.coalesce(
                            Expression.get("icon"),
                            Expression.literal("green_buey")
                        )
                    ),
                    PropertyFactory.iconAnchor(org.maplibre.android.style.layers.Property.ICON_ANCHOR_CENTER),
                    PropertyFactory.iconRotationAlignment(org.maplibre.android.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP),
                    PropertyFactory.iconAllowOverlap(true),
                    PropertyFactory.iconIgnorePlacement(true),
                    PropertyFactory.iconSize(
                        Expression.interpolate(
                            Expression.linear(),
                            Expression.zoom(),
                            Expression.stop(5, 0.08f),
                            Expression.stop(8, 0.10f),
                            Expression.stop(12, 0.15f),
                            Expression.stop(15, 0.20f),
                            Expression.stop(17, 0.25f)
                        )
                    )
                );
            
            style.addLayer(navigationAidLayer);
        }

        // Слой предупреждения (losing) поверх — рисуется поверх, если feature.properties.stale == true
        if (style.getLayer(LAYER_VESSELS_STALE) == null && style.getImage(IMAGE_VESSEL_LOSING) != null) {
            SymbolLayer losingLayer = new SymbolLayer(LAYER_VESSELS_STALE, SOURCE_VESSELS)
                .withFilter(Expression.eq(Expression.get("stale"), true))
                .withProperties(
                    PropertyFactory.iconImage(IMAGE_VESSEL_LOSING),
                    PropertyFactory.iconAnchor(org.maplibre.android.style.layers.Property.ICON_ANCHOR_CENTER),
                    // фиксированно вверх для читаемости
                    PropertyFactory.iconRotationAlignment(org.maplibre.android.style.layers.Property.ICON_ROTATION_ALIGNMENT_VIEWPORT),
                    PropertyFactory.iconRotate(0.0f),
                    PropertyFactory.iconAllowOverlap(true),
                    PropertyFactory.iconIgnorePlacement(true),
                    PropertyFactory.iconSize(
                        Expression.interpolate(
                            Expression.linear(),
                            Expression.zoom(),
                            Expression.stop(5, 0.085f),
                            Expression.stop(8, 0.115f),
                            Expression.stop(12, 0.165f),
                            Expression.stop(15, 0.235f),
                            Expression.stop(17, 0.300f)
                        )
                    )
                );
            if (style.getLayer(LAYER_VESSELS) != null) {
                style.addLayerAbove(losingLayer, LAYER_VESSELS);
            } else {
                style.addLayer(losingLayer);
            }
        }

        // Слой статусных иконок поверх маркеров
        if (style.getLayer(LAYER_STATUS_OVERLAY) == null) {
            SymbolLayer statusLayer = new SymbolLayer(LAYER_STATUS_OVERLAY, SOURCE_VESSELS)
                .withFilter(Expression.has("status_icon"))
                .withProperties(
                    PropertyFactory.iconImage(Expression.get("status_icon")),
                    PropertyFactory.iconAnchor(org.maplibre.android.style.layers.Property.ICON_ANCHOR_CENTER),
                    // фиксированно вверх для читаемости
                    PropertyFactory.iconRotationAlignment(org.maplibre.android.style.layers.Property.ICON_ROTATION_ALIGNMENT_VIEWPORT),
                    PropertyFactory.iconRotate(0.0f),
                    PropertyFactory.iconAllowOverlap(true),
                    PropertyFactory.iconIgnorePlacement(true),
                    PropertyFactory.iconSize(
                        Expression.interpolate(
                            Expression.linear(),
                            Expression.zoom(),
                            Expression.stop(5, 0.085f),
                            Expression.stop(8, 0.115f),
                            Expression.stop(12, 0.165f),
                            Expression.stop(15, 0.235f),
                            Expression.stop(17, 0.300f)
                        )
                    )
                );
            // Над основными маркерами и losing
            if (style.getLayer(LAYER_VESSELS_STALE) != null) {
                style.addLayerAbove(statusLayer, LAYER_VESSELS_STALE);
            } else if (style.getLayer(LAYER_VESSELS) != null) {
                style.addLayerAbove(statusLayer, LAYER_VESSELS);
            } else {
                style.addLayer(statusLayer);
            }
        }
        
        // Создаем источники и слои для трассировки корабля ПОСЛЕ создания маркеров
        createVesselTracingSources();
        
        // Восстанавливаем путь судна после создания слоев
        restoreVesselPath();
        
        // Обновляем дополнительные слои на основе настроек
        updateAdditionalLayers();
        
        Log.d(TAG, "ensureSourcesAndLayers: completed");
    }
    
    
    /**
     * Создает источники и слои для трассировки корабля - НАСТРАИВАЕМАЯ ВЕРСИЯ
     */
    private void createVesselTracingSources() {
        Log.d(TAG, "=== СОЗДАНИЕ СЛОЕВ ТРАССИРОВКИ ===");
        
        try {
            // Получаем настройки из SettingsManager
            int pathColor = settingsManager.getPathColor();
            float pathWidth = settingsManager.getPathWidth();
            int predictionColor = settingsManager.getPredictionColor();
            float predictionWidth = settingsManager.getPredictionWidth();
            
            Log.d(TAG, "Настройки линий: path=" + String.format("#%08X", pathColor) + ":" + pathWidth + 
                      ", prediction=" + String.format("#%08X", predictionColor) + ":" + predictionWidth);
            
            // Отладочная линия удалена
            
            // 1. ЛИНИЯ ПУТИ (путь корабля)
            Log.d(TAG, "Создаем линию пути...");
            // Создаем с двумя одинаковыми точками (корабль)
            String pathGeoJson = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-1.9931215,-0.16609],[-1.9931215,-0.16609]]}}";
            GeoJsonSource pathSource = new GeoJsonSource("path_line_source", pathGeoJson);
            style.addSource(pathSource);
            
            // Извлекаем компоненты цвета пути (ARGB)
            int pathAlpha = (pathColor >> 24) & 0xFF;
            int pathRed = (pathColor >> 16) & 0xFF;
            int pathGreen = (pathColor >> 8) & 0xFF;
            int pathBlue = pathColor & 0xFF;
            
            org.maplibre.android.style.layers.LineLayer pathLayer = new org.maplibre.android.style.layers.LineLayer("path_line_layer", "path_line_source")
                .withProperties(
                    org.maplibre.android.style.layers.PropertyFactory.lineColor(org.maplibre.android.style.expressions.Expression.rgba(pathRed, pathGreen, pathBlue, pathAlpha / 255.0)),
                    org.maplibre.android.style.layers.PropertyFactory.lineWidth(pathWidth),
                    org.maplibre.android.style.layers.PropertyFactory.lineDasharray(new Float[]{5f, 5f}) // СТАТИЧЕСКИЙ ПУНКТИР
                );
            style.addLayerBelow(pathLayer, LAYER_VESSELS);
            Log.d(TAG, "✓ Линия пути создана: цвет=" + String.format("#%08X", pathColor) + ", ширина=" + pathWidth);
            
            // 2. ЛИНИЯ ПРОГНОЗА (прогноз движения)
            Log.d(TAG, "Создаем линию прогноза...");
            // Создаем с двумя точками (корабль и прогноз)
            String predictionGeoJson = "{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-1.9931215,-0.16609],[-1.9931215,-0.16609]]}}";
            GeoJsonSource predictionSource = new GeoJsonSource("prediction_line_source", predictionGeoJson);
            style.addSource(predictionSource);
            
            // Извлекаем компоненты цвета прогноза (ARGB)
            int predictionAlpha = (predictionColor >> 24) & 0xFF;
            int predictionRed = (predictionColor >> 16) & 0xFF;
            int predictionGreen = (predictionColor >> 8) & 0xFF;
            int predictionBlue = predictionColor & 0xFF;
            
            org.maplibre.android.style.layers.LineLayer predictionLayer = new org.maplibre.android.style.layers.LineLayer("prediction_line_layer", "prediction_line_source")
                .withProperties(
                    org.maplibre.android.style.layers.PropertyFactory.lineColor(org.maplibre.android.style.expressions.Expression.rgba(predictionRed, predictionGreen, predictionBlue, predictionAlpha / 255.0)),
                    org.maplibre.android.style.layers.PropertyFactory.lineWidth(predictionWidth)
                    // Убираем пунктир для линии прогноза
                );
            style.addLayerBelow(predictionLayer, LAYER_VESSELS);
            Log.d(TAG, "✓ Линия прогноза создана: цвет=" + String.format("#%08X", predictionColor) + ", ширина=" + predictionWidth);
            
            // 3. ОБЩИЕ СЛОИ ДЛЯ AIS СУДОВ
            Log.d(TAG, "Создаем общие слои для AIS судов...");
            
            // Источник для путей AIS судов
            if (style.getSource(SOURCE_AIS_PATHS) == null) {
                GeoJsonSource aisPathsSource = new GeoJsonSource(SOURCE_AIS_PATHS, emptyFeatureCollection());
                style.addSource(aisPathsSource);
                Log.d(TAG, "Created AIS paths source: " + SOURCE_AIS_PATHS);
            }
            
            // Слой для путей AIS судов
            if (style.getLayer(LAYER_AIS_PATHS) == null) {
                org.maplibre.android.style.layers.LineLayer aisPathsLayer = new org.maplibre.android.style.layers.LineLayer(LAYER_AIS_PATHS, SOURCE_AIS_PATHS)
                    .withProperties(
                        org.maplibre.android.style.layers.PropertyFactory.lineColor(org.maplibre.android.style.expressions.Expression.rgba(pathRed, pathGreen, pathBlue, pathAlpha / 255.0)),
                        org.maplibre.android.style.layers.PropertyFactory.lineWidth(pathWidth),
                        org.maplibre.android.style.layers.PropertyFactory.lineCap(org.maplibre.android.style.layers.Property.LINE_CAP_ROUND),
                        org.maplibre.android.style.layers.PropertyFactory.lineJoin(org.maplibre.android.style.layers.Property.LINE_JOIN_ROUND),
                        org.maplibre.android.style.layers.PropertyFactory.lineDasharray(new Float[]{5f, 5f}) // Пунктир для пройденного пути
                    );
                style.addLayerBelow(aisPathsLayer, LAYER_VESSELS);
                Log.d(TAG, "✓ Общий слой путей AIS создан: " + LAYER_AIS_PATHS);
            }
            
            // Источник для прогнозов AIS судов
            if (style.getSource(SOURCE_AIS_PREDICTIONS) == null) {
                GeoJsonSource aisPredictionsSource = new GeoJsonSource(SOURCE_AIS_PREDICTIONS, emptyFeatureCollection());
                style.addSource(aisPredictionsSource);
                Log.d(TAG, "Created AIS predictions source: " + SOURCE_AIS_PREDICTIONS);
            }
            
            // Слой для прогнозов AIS судов
            if (style.getLayer(LAYER_AIS_PREDICTIONS) == null) {
                org.maplibre.android.style.layers.LineLayer aisPredictionsLayer = new org.maplibre.android.style.layers.LineLayer(LAYER_AIS_PREDICTIONS, SOURCE_AIS_PREDICTIONS)
                    .withProperties(
                        org.maplibre.android.style.layers.PropertyFactory.lineColor(org.maplibre.android.style.expressions.Expression.rgba(predictionRed, predictionGreen, predictionBlue, predictionAlpha / 255.0)),
                        org.maplibre.android.style.layers.PropertyFactory.lineWidth(predictionWidth)
                    );
                style.addLayerBelow(aisPredictionsLayer, LAYER_VESSELS);
                Log.d(TAG, "✓ Общий слой прогнозов AIS создан: " + LAYER_AIS_PREDICTIONS);
            }
            
            Log.d(TAG, "=== ВСЕ СЛОИ СОЗДАНЫ УСПЕШНО ===");
            
        } catch (Exception e) {
            Log.e(TAG, "ОШИБКА при создании слоев: " + e.getMessage(), e);
        }
    }

    /**
     * Запрос обновления GeoJSON с throttling
     */
    private void refreshGeoJson() {
        if (!geoJsonUpdatePending) {
            geoJsonUpdatePending = true;
            geoJsonUpdateHandler.removeCallbacks(geoJsonUpdateRunnable);
            geoJsonUpdateHandler.postDelayed(geoJsonUpdateRunnable, GEOJSON_UPDATE_DELAY);
            Log.d(TAG, "GeoJSON update запланирован на " + GEOJSON_UPDATE_DELAY + "мс");
        }
    }
    
    /**
     * Внутренняя реализация обновления GeoJSON
     */
    private void refreshGeoJsonInternal() {
        if (style == null) return;
        
        try {
            // Проверка валидности стиля перед обращением к источникам
            if (!isStyleValid()) {
                Log.w(TAG, "refreshGeoJson: стиль не валиден, пропускаем обновление");
                return;
            }
            
            GeoJsonSource source = (GeoJsonSource) style.getSource(SOURCE_VESSELS);
            if (source == null) {
                Log.w(TAG, "refreshGeoJson: источник VESSELS не найден, пропускаем обновление");
                return;
            }
            
            // Убираем лишние логи
            // Log.d(TAG, "🔄 refreshGeoJson: обновляем " + idToFeature.size() + " судов");
            long startTime = System.currentTimeMillis();
            
            JSONObject fc = new JSONObject();
            fc.put("type", "FeatureCollection");
            JSONArray features = new JSONArray();
            for (JSONObject f : idToFeature.values()) {
                features.put(f);
            }
            fc.put("features", features);
            source.setGeoJson(fc.toString());
            
            long duration = System.currentTimeMillis() - startTime;
            // Убираем лишние логи
            // Log.d(TAG, "✅ refreshGeoJson: выполнено за " + duration + "мс");
            
            // Логируем использование памяти
            logMemoryUsage();
        } catch (IllegalStateException e) {
            if (e.getMessage() != null && e.getMessage().contains("Calling getSource when a newer style is loading")) {
                Log.w(TAG, "refreshGeoJson: стиль изменяется, пропускаем обновление");
            } else {
                Log.e(TAG, "refreshGeoJson: IllegalStateException " + e.getMessage(), e);
            }
        } catch (Exception e) {
            Log.e(TAG, "refreshGeoJson: " + e.getMessage(), e);
        }
    }

    /**
     * Обновляет источник данных навигационных знаков
     */
    private void refreshNavigationAidsSource() {
        if (style == null) return;
        
        try {
            if (!isStyleValid()) {
                Log.w(TAG, "refreshNavigationAidsSource: стиль не валиден, пропускаем обновление");
                return;
            }
            
            GeoJsonSource source = (GeoJsonSource) style.getSource(SOURCE_NAVIGATION_AIDS);
            if (source == null) {
                Log.w(TAG, "refreshNavigationAidsSource: источник NAVIGATION_AIDS не найден, пропускаем обновление");
                return;
            }
            
            // Создаем FeatureCollection из всех навигационных знаков
            JSONObject featureCollection = new JSONObject();
            featureCollection.put("type", "FeatureCollection");
            
            JSONArray features = new JSONArray();
            for (Map.Entry<String, AISNavigationAid> entry : idToNavigationAid.entrySet()) {
                String mmsi = entry.getKey();
                AISNavigationAid navigationAid = entry.getValue();
                
                // Проверяем, что у нас есть фича для этого навигационного знака
                JSONObject feature = idToFeature.get(mmsi);
                if (feature != null) {
                    features.put(feature);
                }
            }
            
            featureCollection.put("features", features);
            
                // Обновляем источник
                // GeoJsonSource принимает Feature/FeatureCollection/Geometry/String
                // Передаем как строку JSON
                source.setGeoJson(featureCollection.toString());
            
            Log.d(TAG, "refreshNavigationAidsSource: обновлено " + features.length() + " навигационных знаков");
            
        } catch (Exception e) {
            Log.e(TAG, "refreshNavigationAidsSource: " + e.getMessage(), e);
        }
    }
    
    private void logMemoryUsage() {
        Runtime runtime = Runtime.getRuntime();
        long usedMemory = runtime.totalMemory() - runtime.freeMemory();
        long maxMemory = runtime.maxMemory();
        long freeMemory = runtime.freeMemory();
        
        // Убираем лишние логи
        // Log.d(TAG, String.format("Memory: used=%dMB, free=%dMB, max=%dMB (%.1f%%), vessels=%d, paths=%d, predictions=%d", 
        //     usedMemory / 1024 / 1024, 
        //     freeMemory / 1024 / 1024,
        //     maxMemory / 1024 / 1024,
        //     (float) usedMemory / maxMemory * 100,
        //     idToFeature.size(),
        //     aisPathFeatures.size(),
        //     aisPredictionFeatures.size()));
    }
    
    private void refreshAISPathsSource() {
        if (style == null) return;
        
        try {
            // Проверка валидности стиля перед обращением к источникам
            if (!isStyleValid()) {
                Log.w(TAG, "refreshAISPathsSource: стиль не валиден, пропускаем обновление");
                return;
            }
            
            GeoJsonSource source = (GeoJsonSource) style.getSource(SOURCE_AIS_PATHS);
            if (source == null) {
                Log.w(TAG, "refreshAISPathsSource: источник AIS_PATHS не найден, пропускаем обновление");
                return;
            }
            
            JSONObject fc = new JSONObject();
            fc.put("type", "FeatureCollection");
            JSONArray features = new JSONArray();
            for (JSONObject f : aisPathFeatures.values()) {
                features.put(f);
            }
            fc.put("features", features);
            source.setGeoJson(fc.toString());
            Log.d(TAG, "refreshAISPathsSource: updated with " + aisPathFeatures.size() + " paths");
        } catch (IllegalStateException e) {
            if (e.getMessage() != null && e.getMessage().contains("Calling getSource when a newer style is loading")) {
                Log.w(TAG, "refreshAISPathsSource: стиль изменяется, пропускаем обновление");
            } else {
                Log.e(TAG, "refreshAISPathsSource: IllegalStateException " + e.getMessage(), e);
            }
        } catch (Exception e) {
            Log.e(TAG, "refreshAISPathsSource: " + e.getMessage(), e);
        }
    }
    
    private void refreshAISPredictionsSource() {
        if (style == null) return;
        
        try {
            // Проверка валидности стиля перед обращением к источникам
            if (!isStyleValid()) {
                Log.w(TAG, "refreshAISPredictionsSource: стиль не валиден, пропускаем обновление");
                return;
            }
            
            GeoJsonSource source = (GeoJsonSource) style.getSource(SOURCE_AIS_PREDICTIONS);
            if (source == null) {
                Log.w(TAG, "refreshAISPredictionsSource: источник AIS_PREDICTIONS не найден, пропускаем обновление");
                return;
            }
            
            JSONObject fc = new JSONObject();
            fc.put("type", "FeatureCollection");
            JSONArray features = new JSONArray();
            for (JSONObject f : aisPredictionFeatures.values()) {
                features.put(f);
            }
            fc.put("features", features);
            source.setGeoJson(fc.toString());
            Log.d(TAG, "refreshAISPredictionsSource: updated with " + aisPredictionFeatures.size() + " predictions");
        } catch (IllegalStateException e) {
            if (e.getMessage() != null && e.getMessage().contains("Calling getSource when a newer style is loading")) {
                Log.w(TAG, "refreshAISPredictionsSource: стиль изменяется, пропускаем обновление");
            } else {
                Log.e(TAG, "refreshAISPredictionsSource: IllegalStateException " + e.getMessage(), e);
            }
        } catch (Exception e) {
            Log.e(TAG, "refreshAISPredictionsSource: " + e.getMessage(), e);
        }
        
    }

    private JSONObject buildFeature(String id, double lon, double lat, double course, boolean own) throws Exception {
        JSONObject feature = new JSONObject();
        feature.put("type", "Feature");
        feature.put("id", id);

        JSONObject geom = new JSONObject();
        geom.put("type", "Point");
        JSONArray coords = new JSONArray();
        coords.put(lon);
        coords.put(lat);
        geom.put("coordinates", coords);
        feature.put("geometry", geom);

        JSONObject props = new JSONObject();
        props.put("course", normalizeCourse(course));
        props.put("own", own);
        // Для собственного судна используем ownship иконку, для AIS - дефолтную
        props.put("icon", own ? IMAGE_VESSEL_OWN : "target_b_other");
        feature.put("properties", props);

        return feature;
    }

    /**
     * Создает GeoJSON фичу для навигационного знака
     */
    private JSONObject buildNavigationAidFeature(AISNavigationAid navigationAid) throws Exception {
        JSONObject feature = new JSONObject();
        feature.put("type", "Feature");
        feature.put("id", navigationAid.getMmsi());

        JSONObject geom = new JSONObject();
        geom.put("type", "Point");
        JSONArray coords = new JSONArray();
        coords.put(navigationAid.getLongitude());
        coords.put(navigationAid.getLatitude());
        geom.put("coordinates", coords);
        feature.put("geometry", geom);

        JSONObject props = new JSONObject();
        props.put("mmsi", navigationAid.getMmsi());
        props.put("name", navigationAid.getAidName() != null ? navigationAid.getAidName() : "");
        props.put("type", navigationAid.getAidType());
        props.put("typeDescription", navigationAid.getAidTypeDescription());
        props.put("length", navigationAid.getLength());
        props.put("width", navigationAid.getWidth());
        props.put("draft", navigationAid.getDraft());
        props.put("accuracy", navigationAid.isPositionAccuracy());
        props.put("offPosition", navigationAid.isOffPositionIndicator());
        props.put("raim", navigationAid.isRaimFlag());
        
        // Выбираем иконку в зависимости от типа навигационного знака
        String iconName = getNavigationAidIcon(navigationAid.getAidType());
        props.put("icon", iconName);
        
        feature.put("properties", props);
        return feature;
    }

    /**
     * Возвращает имя иконки для навигационного знака в зависимости от типа
     */
    private String getNavigationAidIcon(int aidType) {
        switch (aidType) {
            // Reference point
            case 0:
                return "reference_point"; // Специальная иконка для референсной точки
                
            // RACON (radar transponder marking a navigation hazard)
            case 1:
                return "racon"; // RACON иконка
                
            // Fixed structure off shore, such as oil platforms, wind farms, rigs
            case 2:
                return "platform"; // Платформа
                
            // Spare, Reserved for future use
            case 3:
                return "green_buey"; // Fallback для тестирования
                
            // Light, without sectors
            case 4:
                return "light"; // Маяк
                
            // Light, with sectors
            case 5:
                return "light_sector"; // Маяк с секторами
                
            // Leading Light Front
            case 6:
                return "light_leading_front"; // Передний ведущий маяк
                
            // Leading Light Rear
            case 7:
                return "light_leading_rear"; // Задний ведущий маяк
                
            // Beacon, Cardinal N
            case 8:
                return "buoy_cardinal_n"; // Кардинальный буй Север
                
            // Beacon, Cardinal E
            case 9:
                return "buoy_cardinal_e"; // Кардинальный буй Восток
                
            // Beacon, Cardinal S
            case 10:
                return "buoy_cardinal_s"; // Кардинальный буй Юг
                
            // Beacon, Cardinal W
            case 11:
                return "buoy_cardinal_w"; // Кардинальный буй Запад
                
            // Beacon, Port hand
            case 12:
                return "buoy_port"; // Портовый буй
                
            // Beacon, Starboard hand
            case 13:
                return "buoy_starboard"; // Правобортный буй
                
            // Beacon, Preferred Channel port hand
            case 14:
                return "buoy_preferred_port"; // Предпочтительный канал порт
                
            // Beacon, Preferred Channel starboard hand
            case 15:
                return "buoy_preferred_starboard"; // Предпочтительный канал правый борт
                
            // Beacon, Isolated danger
            case 16:
                return "beacon_isolated_danger"; // Изолированная опасность
                
            // Beacon, Safe water
            case 17:
                return "beacon_safe_water"; // Безопасная вода
                
            // Beacon, Special mark
            case 18:
                return "beacon_special"; // Специальный знак
                
            // Cardinal Mark N
            case 19:
                return "buoy_cardinal_n"; // Кардинальный знак Север
                
            // Cardinal Mark E
            case 20:
                return "buoy_cardinal_e"; // Кардинальный знак Восток
                
            // Cardinal Mark S
            case 21:
                return "buoy_cardinal_s"; // Кардинальный знак Юг
                
            // Cardinal Mark W
            case 22:
                return "buoy_cardinal_w"; // Кардинальный знак Запад
                
            // Port hand Mark
            case 23:
                return "buoy_port"; // Портовый знак
                
            // Starboard hand Mark
            case 24:
                return "buoy_starboard"; // Правобортный знак
                
            // Preferred Channel port hand
            case 25:
                return "buoy_preferred_port"; // Предпочтительный канал порт
                
            // Preferred Channel starboard hand
            case 26:
                return "buoy_preferred_starboard"; // Предпочтительный канал правый борт
                
            // Isolated danger
            case 27:
                return "beacon_isolated_danger"; // Изолированная опасность
                
            // Safe water
            case 28:
                return "beacon_safe_water"; // Безопасная вода
                
            // Special mark
            case 29:
                return "beacon_special"; // Специальный знак
                
            // Light Vessel / LANBY / Rigs
            case 30:
                return "light_vessel"; // Плавучий маяк
                
            default:
                return "green_buey"; // Fallback для тестирования
        }
    }

    /**
     * Создает временный AISVessel из навигационного знака для совместимости с UI
     */
    private AISVessel createTempVesselFromNavigationAid(AISNavigationAid navigationAid) {
        AISVessel tempVessel = new AISVessel(navigationAid.getMmsi());
        tempVessel.setLatitude(navigationAid.getLatitude());
        tempVessel.setLongitude(navigationAid.getLongitude());
        tempVessel.setVesselName(navigationAid.getAidName());
        tempVessel.setVesselClass("Navigation Aid");
        tempVessel.setVesselType(navigationAid.getAidTypeDescription());
        tempVessel.setLength(navigationAid.getLength());
        tempVessel.setWidth(navigationAid.getWidth());
        tempVessel.setDraft(navigationAid.getDraft());
        tempVessel.setPositionAccuracy(navigationAid.isPositionAccuracy());
        tempVessel.setLastUpdate(navigationAid.getLastUpdate());
        
        // Добавляем специальные поля для навигационных знаков
        tempVessel.setDestination("Type: " + navigationAid.getAidType() + " (" + navigationAid.getAidTypeDescription() + ")");
        if (navigationAid.isOffPositionIndicator()) {
            tempVessel.setDestination(tempVessel.getDestination() + " - Off Position");
        }
        if (navigationAid.isRaimFlag()) {
            tempVessel.setDestination(tempVessel.getDestination() + " - RAIM Active");
        }
        
        return tempVessel;
    }

    private double getDisplayCourse(AISVessel v) {
        double hdg = v.getHeading();
        if (!Double.isNaN(hdg) && !Double.isInfinite(hdg)) {
            int h = (int) Math.round(hdg);
            if (h != 511) return normalizeCourse(hdg);
        }
        return normalizeCourse(v.getCourse());
    }

    private double normalizeCourse(double c) {
        return GeoUtils.normalizeAngle(c);
    }

    private String emptyFeatureCollection() {
        return "{\"type\":\"FeatureCollection\",\"features\":[]}";
    }

    private String buildEmptyLineString() {
        return "{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[0,0],[0,0]]}}";
    }
    

    private void appendOwnPathPoint(double lon, double lat) {
        try {
            // Не добавляем нулевые координаты
            if (lon == 0.0 && lat == 0.0) {
                Log.d(TAG, "appendOwnPathPoint: skipping zero coordinates");
                return;
            }
            // Проверяем, изменились ли координаты (строгая проверка на дублирование)
            if (ownPathCoords.length() > 0) {
                JSONArray lastPoint = ownPathCoords.getJSONArray(ownPathCoords.length() - 1);
                double lastLon = lastPoint.getDouble(0);
                double lastLat = lastPoint.getDouble(1);
                
                // Строгая проверка на точное совпадение координат
                if (lon == lastLon && lat == lastLat) {
                    Log.d(TAG, "appendOwnPathPoint: exact duplicate coordinates, skipping");
                    return;
                }
                
                double distance = Math.sqrt(Math.pow(lon - lastLon, 2) + Math.pow(lat - lastLat, 2));
                Log.d(TAG, "appendOwnPathPoint: distance=" + distance + " (threshold=0.00001)");
                if (distance < 0.00001) { // примерно 1 метр
                    Log.d(TAG, "appendOwnPathPoint: vessel not moving, distance=" + distance);
                    return;
                }
            }

            // Лимит точек пути из настроек
            int max = settingsManager.getPathMaxPoints();
            if (ownPathCoords.length() >= max) {
                // удаляем из начала
                ownPathCoords.remove(0);
            }
            JSONArray pt = new JSONArray();
            pt.put(lon);
            pt.put(lat);
            ownPathCoords.put(pt);
            Log.d(TAG, "appendOwnPathPoint: lon=" + lon + ", lat=" + lat + ", size=" + ownPathCoords.length());
        } catch (Exception e) {
            Log.e(TAG, "Error in appendOwnPathPoint", e);
        }
    }

    private void updateOwnPathSource() {
        if (style == null) {
            Log.d(TAG, "updateOwnPathSource: style is null");
            return;
        }
        GeoJsonSource src = (GeoJsonSource) style.getSource(SOURCE_OWN_PATH);
        if (src == null) {
            Log.d(TAG, "updateOwnPathSource: source is null");
            return;
        }
        
        // Проверяем, что слой существует
        if (style.getLayer(LAYER_OWN_PATH) == null) {
            Log.e(TAG, "updateOwnPathSource: LAYER_OWN_PATH layer is null!");
            return;
        }
        
        Log.d(TAG, "updateOwnPathSource: source and layer exist, proceeding...");
        
        try {
            if (ownPathCoords.length() < 2) {
                Log.d(TAG, "updateOwnPathSource: not enough points (" + ownPathCoords.length() + ")");
                return; // нужна минимум пара точек
            }
            
            // Создаем очищенный массив координат без нулевых точек и дубликатов
            JSONArray cleanCoords = new JSONArray();
            double lastLon = Double.NaN;
            double lastLat = Double.NaN;
            
            for (int i = 0; i < ownPathCoords.length(); i++) {
                JSONArray point = ownPathCoords.getJSONArray(i);
                double lon = point.getDouble(0);
                double lat = point.getDouble(1);
                
                // Пропускаем нулевые координаты
                if (lon == 0.0 && lat == 0.0) {
                    continue;
                }
                
                // Пропускаем дубликаты
                if (!Double.isNaN(lastLon) && !Double.isNaN(lastLat) && 
                    lon == lastLon && lat == lastLat) {
                    continue;
                }
                
                cleanCoords.put(point);
                lastLon = lon;
                lastLat = lat;
            }
            
            if (cleanCoords.length() < 2) {
                Log.d(TAG, "updateOwnPathSource: not enough valid points after cleanup (" + cleanCoords.length() + ")");
                return;
            }
            
            Log.d(TAG, "updateOwnPathSource: cleaned " + ownPathCoords.length() + " -> " + cleanCoords.length() + " points");
            
            // Проверяем координаты первых и последних точек
            JSONArray firstPoint = cleanCoords.getJSONArray(0);
            JSONArray lastPoint = cleanCoords.getJSONArray(cleanCoords.length() - 1);
            Log.d(TAG, "updateOwnPathSource: first point=[" + firstPoint.getDouble(0) + "," + firstPoint.getDouble(1) + 
                  "], last point=[" + lastPoint.getDouble(0) + "," + lastPoint.getDouble(1) + "]");
            
            JSONObject line = new JSONObject();
            line.put("type", "Feature");
            JSONObject geom = new JSONObject();
            geom.put("type", "LineString");
            geom.put("coordinates", cleanCoords);
            line.put("geometry", geom);
            
            String geoJsonString = line.toString();
            Log.d(TAG, "updateOwnPathSource: setGeoJson points=" + cleanCoords.length() + ", geoJson length=" + geoJsonString.length());
            Log.d(TAG, "updateOwnPathSource: geoJson preview=" + geoJsonString.substring(0, Math.min(200, geoJsonString.length())) + "...");
            
            // Проверяем, что источник обновился
            try {
                src.setGeoJson(geoJsonString);
                Log.d(TAG, "updateOwnPathSource: GeoJSON successfully set to source");
            } catch (Exception e) {
                Log.e(TAG, "updateOwnPathSource: Error setting GeoJSON to source", e);
            }
        } catch (Exception e) {
            Log.e(TAG, "Error in updateOwnPathSource", e);
        }
    }

    private void updateOwnPredictionSource() {
        if (style == null) {
            Log.d(TAG, "updateOwnPredictionSource: style is null");
            return;
        }
        if (lastOwnVessel == null) {
            Log.d(TAG, "updateOwnPredictionSource: lastOwnVessel is null");
            return;
        }
        GeoJsonSource src = (GeoJsonSource) style.getSource(SOURCE_OWN_PRED);
        if (src == null) {
            Log.d(TAG, "updateOwnPredictionSource: source is null");
            return;
        }
        try {
            // Простейшая экстраполяция: точка сейчас -> точка через horizon секунд по курсу/скорости (узлы)
            double lat = lastOwnVessel.getLatitude();
            double lon = lastOwnVessel.getLongitude();
            double courseDeg = lastOwnVessel.getCourse();
            double speedKn = lastOwnVessel.getSpeed();
            int horizonSec = settingsManager.getPredictionHorizonSec();

            Log.d(TAG, "updateOwnPredictionSource: lat=" + lat + ", lon=" + lon + 
                  ", course=" + courseDeg + ", speed=" + speedKn + ", horizon=" + horizonSec);

            if (horizonSec <= 0 || speedKn <= 0) {
                Log.d(TAG, "updateOwnPredictionSource: clearing prediction (horizon=" + horizonSec + ", speed=" + speedKn + ")");
                src.setGeoJson("{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[0,0],[0,0]]}}");
                return;
            }

            // пересчёт: 1 узел = 1852 м/ч; за t сек = speedKn * 1852 * t / 3600 метров
            double meters = speedKn * 1852.0 * ((double) horizonSec) / 3600.0;
            // приблизительно: 1 градус широты ~ 111320 м; долготы ~ 111320 * cos(lat)
            double dLat = meters / 111320.0;
            double rad = Math.toRadians(courseDeg);
            double dLonMeters = meters * Math.sin(rad);
            double dLatMeters = meters * Math.cos(rad);
            double dLon = dLonMeters / (111320.0 * Math.cos(Math.toRadians(lat)) + 1e-9);
            double lat2 = lat + (dLatMeters / 111320.0);
            double lon2 = lon + dLon;

            JSONArray coords = new JSONArray();
            JSONArray p0 = new JSONArray(); p0.put(lon); p0.put(lat); coords.put(p0);
            JSONArray p1 = new JSONArray(); p1.put(lon2); p1.put(lat2); coords.put(p1);

            JSONObject line = new JSONObject();
            line.put("type", "Feature");
            JSONObject geom = new JSONObject();
            geom.put("type", "LineString");
            geom.put("coordinates", coords);
            line.put("geometry", geom);
            src.setGeoJson(line.toString());
            Log.d(TAG, "updateOwnPredictionSource: prediction updated, distance=" + meters + "m");
            Log.d(TAG, "updateOwnPredictionSource: GeoJSON successfully set to source");
        } catch (Exception e) {
            Log.e(TAG, "Error in updateOwnPredictionSource", e);
        }
    }
    
    
    private void updateAISPathSource(String mmsi) {
        if (style == null) {
            Log.d(TAG, "updateAISPathSource: style is null for " + mmsi);
            return;
        }
        
        // Получаем VesselPathController для этого AIS судна из AppCoordinator
        VesselPathController aisPathController = null;
        if (appCoordinator != null) {
            try {
                aisPathController = appCoordinator.getAISVesselPathController(mmsi);
            } catch (Exception ignore) {}
        }
        
        if (aisPathController == null) {
            Log.d(TAG, "updateAISPathSource: no VesselPathController for " + mmsi);
            return;
        }
        
        // Получаем координаты из VesselPathController
        JSONArray coords = aisPathController.getPathCoordinates();
        if (coords == null || coords.length() < 2) {
            Log.d(TAG, "updateAISPathSource: not enough points (" + (coords != null ? coords.length() : 0) + ") for " + mmsi);
            // Удаляем путь из общего источника
            aisPathFeatures.remove(mmsi);
            refreshAISPathsSource();
            return;
        }
        
        try {
            // Создаем Feature для пути этого судна
            JSONObject line = new JSONObject();
            line.put("type", "Feature");
            line.put("id", mmsi); // Уникальный ID для этого судна
            JSONObject geom = new JSONObject();
            geom.put("type", "LineString");
            geom.put("coordinates", coords);
            line.put("geometry", geom);
            
            // Сохраняем в общем хранилище
            aisPathFeatures.put(mmsi, line);
            
            // Обновляем общий источник
            refreshAISPathsSource();
            
            Log.d(TAG, "updateAISPathSource: updated path for " + mmsi + " with " + coords.length() + " points");
        } catch (Exception e) {
            Log.e(TAG, "Error in updateAISPathSource for " + mmsi, e);
        }
    }

    private org.maplibre.android.style.expressions.Expression colorIntToRgba(int argb) {
        int a = (argb >> 24) & 0xFF;
        int r = (argb >> 16) & 0xFF;
        int g = (argb >> 8) & 0xFF;
        int b = (argb) & 0xFF;
        float af = a / 255f;
        Log.d(TAG, "colorIntToRgba: argb=0x" + Integer.toHexString(argb) + 
              " -> rgba(" + r + "," + g + "," + b + "," + af + ")");
        return org.maplibre.android.style.expressions.Expression.rgba(r, g, b, af);
    }

    private Bitmap getBitmapByName(String name) {
        try {
            int resId = context.getResources().getIdentifier(name, "drawable", context.getPackageName());
            if (resId == 0) return null;
            Drawable d = context.getResources().getDrawable(resId, null);
            if (d == null) return null;
            int w = Math.max(1, d.getIntrinsicWidth());
            int h = Math.max(1, d.getIntrinsicHeight());
            Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(bmp);
            d.setBounds(0, 0, w, h);
            d.draw(c);
            return bmp;
        } catch (Throwable t) {
            return null;
        }
    }

    private void addImageIfAbsent(String id, Bitmap bmp) {
        if (bmp == null) return;
        if (style.getImage(id) == null) {
            style.addImage(id, bmp);
        }
    }

    private String normalizeVesselType(String raw) {
        if (raw == null) return null;
        String r = raw.toLowerCase();
        if (r.contains("cargo")) return TYPE_CARGO;
        if (r.contains("tanker")) return TYPE_TANKER;
        if (r.contains("fishing")) return TYPE_FISHING;
        if (r.contains("passenger")) return TYPE_PASSENGER;
        if (r.contains("pleasure") || r.contains("sailing")) return TYPE_PLEASURE;
        if (r.contains("tug") || r.contains("towing")) return TYPE_TUG;
        if (r.contains("military") || r.contains("navy")) return TYPE_NAVY;
        if (r.contains("pilot")) return TYPE_TUG;
        if (r.contains("law enforcement")) return TYPE_NAVY;
        if (r.contains("high speed craft") || r.contains("hsc")) return TYPE_PASSENGER;
        if (r.contains("other")) return TYPE_OTHER;
        return TYPE_OTHER;
    }

    private String mapStatusToIcon(String raw) {
        if (raw == null) return null;
        String r = raw.toLowerCase();
        if (r.contains("under way using engine") || r.equals("under way")) return STATUS_UNDER_WAY_ENGINE;
        if (r.contains("at anchor")) return STATUS_AT_ANCHOR;
        if (r.contains("moored")) return STATUS_MOORED;
        if (r.contains("engaged in fishing") || r.contains("fishing")) return STATUS_FISHING;
        if (r.contains("under way sailing") || r.contains("sailing")) return STATUS_SAILING;
        return null;
    }

    private final MapLibreMap.OnMapClickListener onMapClickListener = point -> {
        if (maplibreMap == null || style == null) return false;
        try {
            // Получаем экранные координаты клика
            android.graphics.PointF screenPoint = maplibreMap.getProjection().toScreenLocation(point);
            
            // Адаптивный радиус поиска для кликов (немного меньше чем для курсора)
            double zoom;
            try {
                zoom = maplibreMap.getCameraPosition().zoom;
            } catch (Exception e) {
                Log.w(TAG, "onMapClickListener: MapView may be destroyed: " + e.getMessage());
                return false;
            }
            float pixelRadius = calculateAdaptivePixelRadius(zoom) * 0.8f;
            
            // Ищем суда в области клика
            java.util.List<org.maplibre.geojson.Feature> features = maplibreMap.queryRenderedFeatures(
                new android.graphics.RectF(
                    screenPoint.x - pixelRadius, screenPoint.y - pixelRadius,
                    screenPoint.x + pixelRadius, screenPoint.y + pixelRadius
                ), LAYER_VESSELS);
                
            if (features != null && !features.isEmpty()) {
                // Находим ближайшее судно к точке клика
                String closestId = null;
                double minDistance = Double.MAX_VALUE;
                
                for (org.maplibre.geojson.Feature feature : features) {
                    String id = feature.id();
                    if (id != null) {
                        // Получаем координаты судна
                        org.maplibre.android.geometry.LatLng vesselLatLng;
                        if ("own_vessel".equals(id) && lastOwnVessel != null) {
                            vesselLatLng = new org.maplibre.android.geometry.LatLng(
                                lastOwnVessel.getLatitude(), lastOwnVessel.getLongitude());
                        } else {
                            AISVessel vessel = idToAisVessel.get(id);
                            if (vessel != null) {
                                vesselLatLng = new org.maplibre.android.geometry.LatLng(
                                    vessel.getLatitude(), vessel.getLongitude());
                            } else {
                                continue;
                            }
                        }
                        
                        // Вычисляем экранное расстояние
                        android.graphics.PointF vesselScreenPoint = maplibreMap.getProjection()
                            .toScreenLocation(vesselLatLng);
                        double distance = Math.sqrt(
                            Math.pow(screenPoint.x - vesselScreenPoint.x, 2) + 
                            Math.pow(screenPoint.y - vesselScreenPoint.y, 2)
                        );
                        
                        if (distance < minDistance) {
                            minDistance = distance;
                            closestId = id;
                        }
                    }
                }
                
                // Обрабатываем клик по ближайшему судну
                if (closestId != null && minDistance <= pixelRadius) {
                    if ("own_vessel".equals(closestId)) {
                        if (markerClickListener != null) {
                            markerClickListener.onOwnVesselClick(lastOwnVessel);
                        }
                    } else {
                        if (markerClickListener != null) {
                            markerClickListener.onAISVesselClick(idToAisVessel.get(closestId));
                        }
                    }
                    return true;
                }
            }
        } catch (Exception ignored) {}
        return false;
    };

    private void setupClickListener() {
        if (maplibreMap != null) {
            maplibreMap.addOnMapClickListener(onMapClickListener);
        }
    }

    private void preloadImageIfMissing(String drawableName) {
        if (style == null) return;
        if (style.getImage(drawableName) != null) return;
        Bitmap bmp = getBitmapByName(drawableName);
        if (bmp != null) {
            style.addImage(drawableName, bmp);
            Log.d(TAG, "Added style image: " + drawableName + " (" + bmp.getWidth() + "x" + bmp.getHeight() + ")");
        } else {
            Log.w(TAG, "Drawable not found: " + drawableName);
        }
    }

    private void preloadClassTypeIcons(String klass) {
        String[] types = new String[]{TYPE_CARGO, TYPE_TANKER, TYPE_FISHING, TYPE_PASSENGER, TYPE_PLEASURE, TYPE_TUG, TYPE_NAVY, TYPE_OTHER};
        for (String t : types) {
            String name = "target_" + klass + "_" + t;
            preloadImageIfMissing(name);
        }
    }

    private String pickIconNameFor(AISVessel vessel) {
        // Специальные случаи по классу судна
        try {
            String vesselClass = vessel.getVesselClass();
            if (vesselClass != null) {
                String cls = vesselClass.trim().toLowerCase();
                if (cls.contains("base station")) {
                    return "base_station";
                }
                if (cls.contains("navigation aid") || cls.contains("aid-to-navigation") || cls.contains("aton")) {
                    return "green_buey";
                }
            }
            // Обратная совместимость: по типу, если он явно указан как Aid-to-Navigation
            String vesselType = vessel.getVesselType();
            if (vesselType != null && vesselType.trim().toLowerCase().contains("aid-to-navigation")) {
                return "green_buey";
            }
        } catch (Exception ignore) {}
        boolean isClassA = vessel.getVesselClass() != null && vessel.getVesselClass().trim().equalsIgnoreCase("class a");
        String klass = isClassA ? "a" : "b";
        String norm = normalizeTypeToken(vessel.getVesselType());
        String name = "target_" + klass + "_" + norm;
        Log.d(TAG, "pickIconNameFor mmsi=" + vessel.getMmsi() + ", class=" + (isClassA?"A":"B") + ", type=" + norm + " -> " + name);
        return name;
    }

    private String normalizeTypeToken(String raw) {
        if (raw == null) return TYPE_OTHER;
        String r = raw.toLowerCase();
        if (r.contains("cargo")) return TYPE_CARGO;
        if (r.contains("tanker")) return TYPE_TANKER;
        if (r.contains("fishing")) return TYPE_FISHING;
        if (r.contains("passenger")) return TYPE_PASSENGER;
        if (r.contains("pleasure") || r.contains("sailing")) return TYPE_PLEASURE;
        if (r.contains("tug") || r.contains("towing")) return TYPE_TUG;
        if (r.contains("military") || r.contains("navy")) return TYPE_NAVY;
        if (r.contains("pilot")) return TYPE_TUG;
        if (r.contains("law enforcement")) return TYPE_NAVY;
        if (r.contains("high speed craft") || r.contains("hsc")) return TYPE_PASSENGER;
        return TYPE_OTHER;
    }
    
    
    /**
     * Обновляет источник пути для конкретного судна - ПРОСТАЯ ВЕРСИЯ
     */
    private void updateOwnVesselPathSource(String vesselId, JSONArray pathCoords) {
        try {
            // Убираем лишние логи
            // Log.d(TAG, "updateOwnVesselPathSource вызван для vesselId=" + vesselId + ", координат=" + pathCoords.length());
            
            if (maplibreMap == null || maplibreMap.getStyle() == null) {
                Log.w(TAG, "MapLibre map или style is null, cannot update path source");
                return;
            }
            
            Style style = maplibreMap.getStyle();
            String sourceId = "path_line_source";
            
            GeoJsonSource src = (GeoJsonSource) style.getSource(sourceId);
            if (src == null) {
                Log.w(TAG, "Источник пути не найден!");
                return;
            }
            
            // Убираем лишние логи
            // Log.d(TAG, "updateOwnVesselPathSource: получено " + pathCoords.length() + " координат");
            if (pathCoords.length() < 2) {
                Log.d(TAG, "Недостаточно точек пути для отображения: " + pathCoords.length() + " (нужно минимум 2)");
                return;
            }
            
            JSONObject line = new JSONObject();
            line.put("type", "Feature");
            JSONObject geom = new JSONObject();
            geom.put("type", "LineString");
            geom.put("coordinates", pathCoords);
            line.put("geometry", geom);
            
            src.setGeoJson(line.toString());
            
            Log.d(TAG, "✓ Обновлена линия пути: " + pathCoords.length() + " точек");
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка обновления линии пути", e);
        }
    }
    
    /**
     * Обновляет источник пути для AIS судна
     */
    private void updateAISVesselPathSource(String mmsi, JSONArray pathCoords) {
        try {
            if (maplibreMap == null || maplibreMap.getStyle() == null) {
                Log.w(TAG, "MapLibre map или style is null, cannot update AIS path source");
                return;
            }
            
            Style style = maplibreMap.getStyle();
            String sourceId = "ais_paths_source";
            
            GeoJsonSource src = (GeoJsonSource) style.getSource(sourceId);
            if (src == null) {
                Log.w(TAG, "Источник путей AIS не найден!");
                return;
            }
            
            if (pathCoords.length() < 2) {
                Log.d(TAG, "Недостаточно точек пути для AIS судна " + mmsi + ": " + pathCoords.length() + " (нужно минимум 2)");
                return;
            }
            
            // Создаем LineString для пути AIS судна
            JSONObject feature = new JSONObject();
            feature.put("type", "Feature");
            
            JSONObject geometry = new JSONObject();
            geometry.put("type", "LineString");
            geometry.put("coordinates", pathCoords);
            feature.put("geometry", geometry);
            
            JSONObject properties = new JSONObject();
            properties.put("mmsi", mmsi);
            properties.put("vessel_type", "ais");
            feature.put("properties", properties);
            
            // Обновляем источник
            src.setGeoJson(feature.toString());
            Log.d(TAG, "Путь AIS судна " + mmsi + " обновлен: " + pathCoords.length() + " точек");
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка обновления пути AIS судна " + mmsi + ": " + e.getMessage(), e);
        }
    }
    
    /**
     * Сохраняет текущий путь судна
     */
    public void saveVesselPath() {
        if (pathController != null) {
            Log.d(TAG, "Сохранение пути судна: " + pathController.getPathInfo());
        }
    }
    
    /**
     * Восстанавливает путь судна после перезапуска
     */
    public void restoreVesselPath() {
        if (pathController != null && pathController.getPathPointsCount() >= 2) {
            JSONArray pathCoords = pathController.getPathCoordinatesWithSpeed();
            // Переносим восстановление пути на UI поток
            uiHandler.post(() -> updateOwnVesselPathSource("own_vessel", pathCoords));
            Log.d(TAG, "Восстановлен путь судна: " + pathController.getPathInfo());
        } else if (pathController != null) {
            Log.d(TAG, "Недостаточно точек для восстановления пути: " + pathController.getPathPointsCount() + " (нужно минимум 2)");
        }
    }
    
    /**
     * Очищает путь судна
     */
    @Override
    public void clearVesselPath() {
        Log.i(TAG, "MapLibreMapImpl.clearVesselPath() вызван");
        
        if (pathController != null) {
            Log.i(TAG, "Очищаем VesselPathController");
            pathController.clearPath();
            
            // Очищаем буфер координат пути в памяти
            synchronized (ownPathCoords) {
                int coordsCount = ownPathCoords.length();
                while (ownPathCoords.length() > 0) {
                    ownPathCoords.remove(0);
                }
                Log.i(TAG, "Очищено " + coordsCount + " координат из буфера ownPathCoords");
            }
            
            // Обновляем источник с пустыми координатами на UI потоке
            JSONArray emptyCoords = new JSONArray();
            uiHandler.post(() -> updateOwnVesselPathSource("own_vessel", emptyCoords));
            Log.i(TAG, "Путь судна очищен из памяти, SharedPreferences и карты");
        } else {
            Log.w(TAG, "pathController is null, не можем очистить путь");
        }
    }
    
    /**
     * Обновляет все пути судов на карте
     */
    @Override
    public void updateAllVesselPaths() {
        Log.d(TAG, "MapLibreMapImpl.updateAllVesselPaths() вызван");
        
        // Обновляем путь собственного судна
        if (pathController != null) {
            restoreVesselPath();
        }
        
        // Обновляем пути AIS судов
        updateAISVesselPaths();
        
        Log.d(TAG, "Все пути судов обновлены");
    }
    
    /**
     * Обновляет пути AIS судов
     */
    private void updateAISVesselPaths() {
        if (appCoordinator == null) {
            Log.w(TAG, "appCoordinator is null, не можем обновить пути AIS судов");
            return;
        }
        
        try {
            // Получаем все AIS суда
            List<AISVessel> aisVessels = appCoordinator.getAISVessels();
            Log.d(TAG, "Обновляем пути для " + aisVessels.size() + " AIS судов");
            
            for (AISVessel vessel : aisVessels) {
                String mmsi = vessel.getMmsi();
                VesselPathController aisPathController = appCoordinator.getAISVesselPathController(mmsi);
                
                if (aisPathController != null) {
                    // Обновляем путь этого AIS судна
                    updateAISVesselPath(mmsi, aisPathController);
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Ошибка обновления путей AIS судов: " + e.getMessage(), e);
        }
    }
    
    /**
     * Обновляет путь конкретного AIS судна
     */
    private void updateAISVesselPath(String mmsi, VesselPathController pathController) {
        try {
            // Получаем координаты пути из VesselPathController
            JSONArray pathCoords = pathController.getPathCoordinates();
            
            if (pathCoords != null && pathCoords.length() >= 2) {
                // Обновляем источник на карте
                updateAISVesselPathSource(mmsi, pathCoords);
                Log.d(TAG, "Путь AIS судна " + mmsi + " обновлен: " + pathCoords.length() + " точек");
            }
        } catch (Exception e) {
            Log.e(TAG, "Ошибка обновления пути AIS судна " + mmsi + ": " + e.getMessage(), e);
        }
    }
    
    /**
     * Получает информацию о пути судна
     */
    public String getVesselPathInfo() {
        if (pathController != null) {
            return pathController.getPathInfo();
        }
        return "Контроллер пути не инициализирован";
    }
    
    /**
     * Обновляет источник прогноза для конкретного судна - ПРОСТАЯ ВЕРСИЯ
     */
    private void updateOwnVesselPredictionSource(String vesselId, Vessel vessel) {
        try {
            if (maplibreMap == null || maplibreMap.getStyle() == null || vessel == null) {
                return;
            }
            
            Style style = maplibreMap.getStyle();
            String sourceId = "prediction_line_source";
            
            GeoJsonSource src = (GeoJsonSource) style.getSource(sourceId);
            if (src == null) {
                Log.w(TAG, "Источник прогноза не найден!");
                return;
            }
            
            double lat = vessel.getLatitude();
            double lon = vessel.getLongitude();
            double courseDeg = vessel.getCourse();
            double speedKn = vessel.getSpeed();
            int horizonSec = settingsManager.getPredictionHorizonSec();

            if (horizonSec <= 0 || speedKn <= 0) {
                Log.d(TAG, "Очищаем прогноз (horizon=" + horizonSec + ", speed=" + speedKn + ")");
                src.setGeoJson("{\"type\":\"Feature\",\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[0,0],[0,0]]}}");
                return;
            }

            // Расчет прогноза
            double meters = speedKn * 1852.0 * ((double) horizonSec) / 3600.0;
            double rad = Math.toRadians(courseDeg);
            double dLonMeters = meters * Math.sin(rad);
            double dLatMeters = meters * Math.cos(rad);
            double dLon = dLonMeters / (111320.0 * Math.cos(Math.toRadians(lat)) + 1e-9);
            double lat2 = lat + (dLatMeters / 111320.0);
            double lon2 = lon + dLon;

            JSONArray coords = new JSONArray();
            JSONArray p0 = new JSONArray(); p0.put(lon); p0.put(lat); coords.put(p0);
            JSONArray p1 = new JSONArray(); p1.put(lon2); p1.put(lat2); coords.put(p1);

            JSONObject line = new JSONObject();
            line.put("type", "Feature");
            JSONObject geom = new JSONObject();
            geom.put("type", "LineString");
            geom.put("coordinates", coords);
            line.put("geometry", geom);
            
            src.setGeoJson(line.toString());
            Log.d(TAG, "✓ Обновлена линия прогноза: расстояние=" + meters + "м");
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка обновления линии прогноза", e);
        }
    }
    
    /**
     * Обновляет источник прогноза для AIS судна
     */
    private void updateAISVesselPredictionSource(String mmsi, AISVessel vessel) {
        try {
            if (maplibreMap == null || maplibreMap.getStyle() == null || vessel == null) {
                return;
            }
            
            double lat = vessel.getLatitude();
            double lon = vessel.getLongitude();
            double courseDeg = getDisplayCourse(vessel);
            double speedKn = vessel.getSpeed();
            int horizonSec = settingsManager.getPredictionHorizonSec();

            if (horizonSec <= 0 || speedKn <= 0) {
                Log.d(TAG, "Очищаем прогноз для AIS " + mmsi + " (horizon=" + horizonSec + ", speed=" + speedKn + ")");
                aisPredictionFeatures.remove(mmsi);
                refreshAISPredictionsSource();
                return;
            }

            // Расчет прогноза
            double meters = speedKn * 1852.0 * ((double) horizonSec) / 3600.0;
            double rad = Math.toRadians(courseDeg);
            double dLonMeters = meters * Math.sin(rad);
            double dLatMeters = meters * Math.cos(rad);
            double dLon = dLonMeters / (111320.0 * Math.cos(Math.toRadians(lat)) + 1e-9);
            double lat2 = lat + (dLatMeters / 111320.0);
            double lon2 = lon + dLon;

            JSONArray coords = new JSONArray();
            JSONArray p0 = new JSONArray(); p0.put(lon); p0.put(lat); coords.put(p0);
            JSONArray p1 = new JSONArray(); p1.put(lon2); p1.put(lat2); coords.put(p1);

            // Создаем Feature для прогноза этого судна
            JSONObject line = new JSONObject();
            line.put("type", "Feature");
            line.put("id", mmsi); // Уникальный ID для этого судна
            JSONObject geom = new JSONObject();
            geom.put("type", "LineString");
            geom.put("coordinates", coords);
            line.put("geometry", geom);
            
            // Сохраняем в общем хранилище
            aisPredictionFeatures.put(mmsi, line);
            
            // Обновляем общий источник
            refreshAISPredictionsSource();
            
            Log.d(TAG, "✓ Обновлена линия прогноза для AIS " + mmsi + ": расстояние=" + meters + "м");
            
        } catch (Exception e) {
            Log.e(TAG, "Ошибка обновления линии прогноза для AIS " + mmsi, e);
        }
    }
    
    /**
     * Удаляет путь для AIS судна
     */
    private void removeAISVesselPath(String mmsi) {
        try {
            // Удаляем из общего хранилища
            aisPathFeatures.remove(mmsi);
            // Обновляем общий источник
            refreshAISPathsSource();
            Log.d(TAG, "Removed AIS path for " + mmsi);
        } catch (Exception e) {
            Log.e(TAG, "Ошибка удаления пути для AIS " + mmsi, e);
        }
    }
    
    /**
     * Удаляет прогноз для AIS судна
     */
    private void removeAISVesselPrediction(String mmsi) {
        try {
            // Удаляем из общего хранилища
            aisPredictionFeatures.remove(mmsi);
            // Обновляем общий источник
            refreshAISPredictionsSource();
            Log.d(TAG, "Removed AIS prediction for " + mmsi);
        } catch (Exception e) {
            Log.e(TAG, "Ошибка удаления прогноза для AIS " + mmsi, e);
        }
    }
    
    /**
     * Очищает все пути AIS судов
     */
    private void clearAllAISVesselPaths() {
        try {
            // Очищаем общее хранилище
            aisPathFeatures.clear();
            // Обновляем общий источник
            refreshAISPathsSource();
            Log.d(TAG, "All AIS vessel paths cleared");
        } catch (Exception e) {
            Log.e(TAG, "Ошибка очистки всех путей AIS судов", e);
        }
    }
    
    /**
     * Очищает все прогнозы AIS судов
     */
    private void clearAllAISVesselPredictions() {
        try {
            // Очищаем общее хранилище
            aisPredictionFeatures.clear();
            // Обновляем общий источник
            refreshAISPredictionsSource();
            Log.d(TAG, "All AIS vessel predictions cleared");
        } catch (Exception e) {
            Log.e(TAG, "Ошибка очистки всех прогнозов AIS судов", e);
        }
    }

    /**
     * Проверка валидности текущего стиля карты
     * @return true если стиль валиден и готов к работе
     */
    private boolean isStyleValid() {
        try {
            // Проверяем состояние стиля
            if (style == null) {
                return false;
            }
            
            // Проверяем, что стиль полностью загружен
            if (!style.isFullyLoaded()) {
                return false;
            }
            
            // Дополнительная проверка - пытаемся получить доступ к методам стиля
            // Это поможет выявить состояние "newer style is loading"
            style.getSources();
            
            // Если мы дошли до этого места, стиль валиден
            return true;
        } catch (IllegalStateException e) {
            // Это именно та ошибка, которую мы ловим
            Log.d(TAG, "isStyleValid: стиль в процессе загрузки: " + e.getMessage());
            return false;
        } catch (Exception e) {
            // Если произошло другое исключение, стиль не валиден
            Log.d(TAG, "isStyleValid: ошибка проверки стиля: " + e.getMessage());
            return false;
        }
    }
    
    @Override
    public void showCursor() {
        try {
            if (cursorOverlay == null) {
                cursorOverlay = new com.grigowashere.aismap.view.CursorOverlay(context);
            }
            // Убедимся, что overlay добавлен в иерархию и находится поверх карты
            if (mapView instanceof android.view.ViewGroup) {
                android.view.ViewGroup parent = (android.view.ViewGroup) mapView;
                android.view.View v = cursorOverlay.getView();
                if (v.getParent() != parent) {
                    if (v.getParent() instanceof android.view.ViewGroup) {
                        ((android.view.ViewGroup) v.getParent()).removeView(v);
                    }
                    android.view.ViewGroup.LayoutParams lp = new android.view.ViewGroup.LayoutParams(
                        android.view.ViewGroup.LayoutParams.MATCH_PARENT,
                        android.view.ViewGroup.LayoutParams.MATCH_PARENT
                    );
                    parent.addView(v, lp);
                }
                v.bringToFront();
                v.setTranslationZ(1f);
            }
            cursorOverlay.showCursor();
            // Обновим размеры, координаты и попробуем восстановить панель AIS
            updateScreenDimensions();
            updateCursorFromMapCenter();
            // Подпишемся на движение камеры (на случай если слушатель потерян)
            setupMapMovementListener();
            forceCheckAisVesselUnderCursor();
        } catch (Exception ignore) {}
    }
    
    @Override
    public void hideCursor() {
        if (cursorOverlay != null) {
            cursorOverlay.hideCursor();
            clearAisVesselInfo();
        }
    }
    
    @Override
    public void updateCursorCoordinates(double latitude, double longitude) {
        if (cursorOverlay != null) {
            cursorOverlay.updateCursorCoordinates(latitude, longitude);
        }
    }
    
    @Override
    public void updateCursorFromMapCenter() {
        if (cursorOverlay != null && mapView != null) {
            try {
                // Получаем координаты центра карты (если карта уже готова)
                org.maplibre.android.geometry.LatLng center = maplibreMap != null ? maplibreMap.getCameraPosition().target : null;
                Log.d(TAG, String.format("updateCursorFromMapCenter: center=%.6f,%.6f", 
                    center != null ? center.getLatitude() : 0,
                    center != null ? center.getLongitude() : 0));
                
                if (center != null) {
                    cursorOverlay.updateCursorCoordinates(center.getLatitude(), center.getLongitude());
                    // Проверяем, есть ли AIS судно под курсором
                    checkAisVesselUnderCursor(center);
                }
            } catch (Exception e) {
                Log.w(TAG, "updateCursorFromMapCenter: MapView may be destroyed: " + e.getMessage());
            }
        } else {
            Log.d(TAG, "updateCursorFromMapCenter: cursorOverlay или mapView равны null");
        }
    }

    /**
     * Публичный метод для принудительного удаления cursor overlay из вью-иерархии
     * Используется активностью в onPause/onStop, чтобы исключить наслоение при возврате
     */
    public void removeCursorOverlay() {
        try {
            if (cursorOverlay != null) {
                cursorOverlay.hideCursor();
            }
            if (mapView != null) {
                // Удаляем из родителя MapView
                ViewParent mapParent = mapView.getParent();
                if (mapParent instanceof ViewGroup) {
                    ViewGroup parent = (ViewGroup) mapParent;
                    for (int i = parent.getChildCount() - 1; i >= 0; i--) {
                        View child = parent.getChildAt(i);
                        Object tag = child.getTag();
                        if (tag != null && "cursor_overlay".equals(tag)) {
                            parent.removeViewAt(i);
                        }
                    }
                }
                // На всякий случай: уберём из самого MapView (если когда-то добавлялся туда)
                if (mapView instanceof ViewGroup) {
                    ViewGroup mv = (ViewGroup) mapView;
                    for (int i = mv.getChildCount() - 1; i >= 0; i--) {
                        View child = mv.getChildAt(i);
                        Object tag = child.getTag();
                        if (tag != null && "cursor_overlay".equals(tag)) {
                            mv.removeViewAt(i);
                        }
                    }
                }
            }
        } catch (Exception ignore) {}
    }
    
    /**
     * Проверяет, есть ли AIS судно под курсором (в центре экрана)
     */
    private void checkAisVesselUnderCursor(org.maplibre.android.geometry.LatLng center) {
        if (maplibreMap == null || style == null) {
            Log.d(TAG, "checkAisVesselUnderCursor: maplibreMap или style равны null");
            return;
        }
        
        // Проверяем, что стиль готов к работе
        if (!isStyleValid()) {
            Log.d(TAG, "checkAisVesselUnderCursor: стиль не готов, пропускаем поиск");
            return;
        }
        
        try {
            // Получаем экранные координаты центра
            android.graphics.PointF screenPoint = maplibreMap.getProjection().toScreenLocation(center);
            
            // Адаптивный радиус поиска в зависимости от масштаба карты
            double zoom;
            try {
                zoom = maplibreMap.getCameraPosition().zoom;
            } catch (Exception e) {
                Log.w(TAG, "checkAisVesselUnderCursor: MapView may be destroyed: " + e.getMessage());
                return;
            }
            float pixelRadius = calculateAdaptivePixelRadius(zoom);
            
            Log.d(TAG, String.format("checkAisVesselUnderCursor: center=%.6f,%.6f, screen=%.1f,%.1f, zoom=%.2f, radius=%.1f", 
                center.getLatitude(), center.getLongitude(), screenPoint.x, screenPoint.y, zoom, pixelRadius));
            
            // Создаем область поиска
            android.graphics.RectF searchRect = new android.graphics.RectF(
                screenPoint.x - pixelRadius, screenPoint.y - pixelRadius,
                screenPoint.x + pixelRadius, screenPoint.y + pixelRadius
            );
            
            // Сохраняем для отладочной визуализации
            if (debugMode) {
                lastSearchRect = new android.graphics.RectF(searchRect);
                updateDebugVisualization();
            }
            
            Log.d(TAG, String.format("checkAisVesselUnderCursor: searchRect=[%.1f,%.1f,%.1f,%.1f]", 
                searchRect.left, searchRect.top, searchRect.right, searchRect.bottom));
            
            // Ищем AIS суда и навигационные знаки в адаптивном радиусе от центра
            java.util.List<org.maplibre.geojson.Feature> vesselFeatures = maplibreMap.queryRenderedFeatures(searchRect, LAYER_VESSELS);
            java.util.List<org.maplibre.geojson.Feature> aidFeatures = maplibreMap.queryRenderedFeatures(searchRect, LAYER_NAVIGATION_AIDS);
            
            // Объединяем результаты
            java.util.List<org.maplibre.geojson.Feature> features = new java.util.ArrayList<>();
            if (vesselFeatures != null) features.addAll(vesselFeatures);
            if (aidFeatures != null) features.addAll(aidFeatures);
            
            Log.d(TAG, String.format("checkAisVesselUnderCursor: найдено %d судов и %d навигационных знаков в основном поиске", 
                vesselFeatures != null ? vesselFeatures.size() : 0, aidFeatures != null ? aidFeatures.size() : 0));
            
            // Если не нашли в основном радиусе, попробуем расширенный поиск
            if (features.isEmpty() && pixelRadius < 150) {
                android.graphics.RectF expandedRect = new android.graphics.RectF(
                    screenPoint.x - 150, screenPoint.y - 150,
                    screenPoint.x + 150, screenPoint.y + 150
                );
                vesselFeatures = maplibreMap.queryRenderedFeatures(expandedRect, LAYER_VESSELS);
                aidFeatures = maplibreMap.queryRenderedFeatures(expandedRect, LAYER_NAVIGATION_AIDS);
                
                features.clear();
                if (vesselFeatures != null) features.addAll(vesselFeatures);
                if (aidFeatures != null) features.addAll(aidFeatures);
                
                Log.d(TAG, String.format("checkAisVesselUnderCursor: найдено %d судов и %d навигационных знаков в расширенном поиске", 
                    vesselFeatures != null ? vesselFeatures.size() : 0, aidFeatures != null ? aidFeatures.size() : 0));
            }
            
            if (features != null && !features.isEmpty()) {
                Log.d(TAG, "checkAisVesselUnderCursor: обрабатываем найденные features");
                
                // Находим ближайшее AIS судно с учетом как экранного, так и географического расстояния
                AISVessel closestVessel = null;
                double minScreenDistance = Double.MAX_VALUE;
                double minGeoDistance = Double.MAX_VALUE;
                
                for (org.maplibre.geojson.Feature feature : features) {
                    String id = feature.id();
                    Log.d(TAG, String.format("checkAisVesselUnderCursor: проверяем feature с id=%s", id));
                    
                    if (id != null && !"own_vessel".equals(id)) {
                        // Проверяем AIS судно
                        AISVessel vessel = idToAisVessel.get(id);
                        if (vessel != null) {
                            // Вычисляем географическое расстояние от центра до судна
                            double geoDistance = GeoUtils.calculateDistance(
                                center.getLatitude(), center.getLongitude(),
                                vessel.getLatitude(), vessel.getLongitude()
                            );
                            
                            // Вычисляем экранное расстояние
                            android.graphics.PointF vesselScreenPoint = maplibreMap.getProjection()
                                .toScreenLocation(new org.maplibre.android.geometry.LatLng(
                                    vessel.getLatitude(), vessel.getLongitude()));
                            double screenDistance = Math.sqrt(
                                Math.pow(screenPoint.x - vesselScreenPoint.x, 2) + 
                                Math.pow(screenPoint.y - vesselScreenPoint.y, 2)
                            );
                            
                            Log.d(TAG, String.format("checkAisVesselUnderCursor: судно %s - geoDistance=%.1f м, screenDistance=%.1f пикс", 
                                id, geoDistance, screenDistance));
                            
                            // Приоритет отдаем экранному расстоянию, но учитываем и географическое
                            boolean isBetterCandidate = false;
                            if (closestVessel == null) {
                                isBetterCandidate = true;
                            } else if (screenDistance < minScreenDistance * 0.8) {
                                // Если экранное расстояние значительно меньше
                                isBetterCandidate = true;
                            } else if (screenDistance <= minScreenDistance * 1.2 && geoDistance < minGeoDistance) {
                                // Если экранное расстояние примерно равно, но географическое меньше
                                isBetterCandidate = true;
                            }
                            
                            if (isBetterCandidate) {
                                Log.d(TAG, String.format("checkAisVesselUnderCursor: выбираем судно %s как лучший кандидат", id));
                                minScreenDistance = screenDistance;
                                minGeoDistance = geoDistance;
                                closestVessel = vessel;
                            }
                        } else {
                            Log.d(TAG, String.format("checkAisVesselUnderCursor: судно с id=%s не найдено в idToAisVessel", id));
                        }
                        
                        // Проверяем навигационный знак
                        AISNavigationAid navigationAid = idToNavigationAid.get(id);
                        if (navigationAid != null) {
                            // Вычисляем географическое расстояние от центра до навигационного знака
                            double geoDistance = GeoUtils.calculateDistance(
                                center.getLatitude(), center.getLongitude(),
                                navigationAid.getLatitude(), navigationAid.getLongitude()
                            );
                            
                            // Вычисляем экранное расстояние
                            android.graphics.PointF aidScreenPoint = maplibreMap.getProjection()
                                .toScreenLocation(new org.maplibre.android.geometry.LatLng(
                                    navigationAid.getLatitude(), navigationAid.getLongitude()));
                            double screenDistance = Math.sqrt(
                                Math.pow(screenPoint.x - aidScreenPoint.x, 2) + 
                                Math.pow(screenPoint.y - aidScreenPoint.y, 2)
                            );
                            
                            Log.d(TAG, String.format("checkAisVesselUnderCursor: навигационный знак %s - geoDistance=%.1f м, screenDistance=%.1f пикс", 
                                id, geoDistance, screenDistance));
                            
                            // Приоритет отдаем экранному расстоянию, но учитываем и географическое
                            boolean isBetterCandidate = false;
                            if (closestVessel == null) {
                                isBetterCandidate = true;
                            } else if (screenDistance < minScreenDistance * 0.8) {
                                // Если экранное расстояние значительно меньше
                                isBetterCandidate = true;
                            } else if (screenDistance <= minScreenDistance * 1.2 && geoDistance < minGeoDistance) {
                                // Если экранное расстояние примерно равно, но географическое меньше
                                isBetterCandidate = true;
                            }
                            
                            if (isBetterCandidate) {
                                Log.d(TAG, String.format("checkAisVesselUnderCursor: выбираем навигационный знак %s как лучший кандидат", id));
                                minScreenDistance = screenDistance;
                                minGeoDistance = geoDistance;
                                // Для навигационных знаков создаем временный AISVessel для совместимости
                                closestVessel = createTempVesselFromNavigationAid(navigationAid);
                            }
                        }
                    } else if ("own_vessel".equals(id)) {
                        Log.d(TAG, "checkAisVesselUnderCursor: пропускаем собственное судно");
                    }
                }
                
                // Адаптивный порог для географического расстояния в зависимости от масштаба
                double maxGeoDistance = calculateAdaptiveGeoRadius(zoom);
                
                Log.d(TAG, String.format("checkAisVesselUnderCursor: лучший кандидат - screenDistance=%.1f (лимит %.1f), geoDistance=%.1f (лимит %.1f)", 
                    minScreenDistance, pixelRadius * 1.5, minGeoDistance, maxGeoDistance));
                
                // Если нашли судно в допустимом радиусе, показываем информацию
                if (closestVessel != null && 
                    (minScreenDistance <= pixelRadius * 1.5 || minGeoDistance <= maxGeoDistance)) {
                    Log.d(TAG, String.format("checkAisVesselUnderCursor: показываем информацию о судне %s", closestVessel.getMmsi()));
                    setAisVesselInfo(closestVessel);
                } else {
                    Log.d(TAG, "checkAisVesselUnderCursor: судно не прошло проверку расстояния, очищаем информацию");
                    clearAisVesselInfo();
                }
            } else {
                Log.d(TAG, "checkAisVesselUnderCursor: features пустой, очищаем информацию");
                clearAisVesselInfo();
            }
        } catch (Exception e) {
            // В случае ошибки очищаем информацию
            Log.e(TAG, "checkAisVesselUnderCursor: ошибка при поиске судна", e);
            clearAisVesselInfo();
        }
    }
    
    /**
     * Вычисляет адаптивный радиус поиска в пикселях в зависимости от масштаба карты
     */
    private float calculateAdaptivePixelRadius(double zoom) {
        // Базовый радиус 80 пикселей для среднего масштаба (zoom ~12)
        // При увеличении масштаба радиус увеличивается, при уменьшении - уменьшается
        float baseRadius = 80f;
        float zoomFactor = (float) Math.pow(1.2, zoom - 12);
        float radius = baseRadius * zoomFactor;
        
        // Ограничиваем радиус разумными пределами
        return Math.max(40f, Math.min(200f, radius));
    }
    
    /**
     * Вычисляет адаптивный радиус поиска в метрах в зависимости от масштаба карты
     */
    private double calculateAdaptiveGeoRadius(double zoom) {
        // Базовый радиус 200 метров для среднего масштаба (zoom ~12)
        // При увеличении масштаба радиус уменьшается, при уменьшении - увеличивается
        double baseRadius = 200.0;
        double zoomFactor = Math.pow(0.7, zoom - 12);
        double radius = baseRadius * zoomFactor;
        
        // Ограничиваем радиус разумными пределами (от 50 метров до 2 км)
        return Math.max(50.0, Math.min(2000.0, radius));
    }
    

    @Override
    public void setAisVesselInfo(com.grigowashere.aismap.models.AISVessel vessel) {
        Log.d(TAG, String.format("setAisVesselInfo: устанавливаем информацию о судне %s", 
            vessel != null ? vessel.getMmsi() : "null"));
        if (cursorOverlay != null) {
            cursorOverlay.setAisVesselInfo(vessel);
        } else {
            Log.d(TAG, "setAisVesselInfo: cursorOverlay равен null");
        }
    }

    @Override
    public void clearAisVesselInfo() {
        Log.d(TAG, "clearAisVesselInfo: очищаем информацию о судне");
        if (cursorOverlay != null) {
            cursorOverlay.clearAisVesselInfo();
        } else {
            Log.d(TAG, "clearAisVesselInfo: cursorOverlay равен null");
        }
    }
    
    /**
     * Принудительно проверяет AIS судно под курсором (для восстановления панели после возврата в активность)
     */
    public void forceCheckAisVesselUnderCursor() {
        if (maplibreMap != null) {
            try {
                org.maplibre.android.geometry.LatLng center = maplibreMap.getCameraPosition().target;
                Log.d(TAG, String.format("forceCheckAisVesselUnderCursor: принудительная проверка центра=%.6f,%.6f", 
                    center.getLatitude(), center.getLongitude()));
                checkAisVesselUnderCursor(center);
            } catch (Exception e) {
                Log.w(TAG, "forceCheckAisVesselUnderCursor: MapView may be destroyed: " + e.getMessage());
            }
        } else {
            Log.d(TAG, "forceCheckAisVesselUnderCursor: maplibreMap равен null");
        }
    }
    
    /**
     * Обновляет размеры экрана для курсора (например, при повороте устройства)
     */
    public void updateScreenDimensions() {
        if (cursorOverlay != null && mapView != null) {
            mapView.post(() -> {
                int width = mapView.getWidth();
                int height = mapView.getHeight();
                if (width > 0 && height > 0) {
                    cursorOverlay.setScreenDimensions(width, height);
                    Log.d(TAG, String.format("Обновлены размеры экрана для курсора: %dx%d", width, height));
                }
            });
        }
    }
    
    /**
     * Обновляет отладочную визуализацию области поиска
     */
    private void updateDebugVisualization() {
        if (!debugMode || lastSearchRect == null || maplibreMap == null || style == null) {
            return;
        }
        
        // Проверяем, что стиль готов к работе
        if (!isStyleValid()) {
            Log.d(TAG, "updateDebugVisualization: стиль не готов, пропускаем визуализацию");
            return;
        }
        
        try {
            // Удаляем предыдущий отладочный слой если есть
            if (style.getLayer("debug-search-area") != null) {
                style.removeLayer("debug-search-area");
            }
            if (style.getSource("debug-search-area") != null) {
                style.removeSource("debug-search-area");
            }
            
            // Конвертируем экранные координаты обратно в географические
            org.maplibre.android.geometry.LatLng topLeft = maplibreMap.getProjection()
                .fromScreenLocation(new android.graphics.PointF(lastSearchRect.left, lastSearchRect.top));
            org.maplibre.android.geometry.LatLng topRight = maplibreMap.getProjection()
                .fromScreenLocation(new android.graphics.PointF(lastSearchRect.right, lastSearchRect.top));
            org.maplibre.android.geometry.LatLng bottomRight = maplibreMap.getProjection()
                .fromScreenLocation(new android.graphics.PointF(lastSearchRect.right, lastSearchRect.bottom));
            org.maplibre.android.geometry.LatLng bottomLeft = maplibreMap.getProjection()
                .fromScreenLocation(new android.graphics.PointF(lastSearchRect.left, lastSearchRect.bottom));
            
            // Создаем полигон для отладочной области
            java.util.List<org.maplibre.geojson.Point> coordinates = new java.util.ArrayList<>();
            coordinates.add(org.maplibre.geojson.Point.fromLngLat(topLeft.getLongitude(), topLeft.getLatitude()));
            coordinates.add(org.maplibre.geojson.Point.fromLngLat(topRight.getLongitude(), topRight.getLatitude()));
            coordinates.add(org.maplibre.geojson.Point.fromLngLat(bottomRight.getLongitude(), bottomRight.getLatitude()));
            coordinates.add(org.maplibre.geojson.Point.fromLngLat(bottomLeft.getLongitude(), bottomLeft.getLatitude()));
            coordinates.add(org.maplibre.geojson.Point.fromLngLat(topLeft.getLongitude(), topLeft.getLatitude())); // Замыкаем полигон
            
            java.util.List<java.util.List<org.maplibre.geojson.Point>> polygon = new java.util.ArrayList<>();
            polygon.add(coordinates);
            
            org.maplibre.geojson.Polygon debugPolygon = org.maplibre.geojson.Polygon.fromLngLats(polygon);
            org.maplibre.geojson.Feature debugFeature = org.maplibre.geojson.Feature.fromGeometry(debugPolygon);
            
            // Создаем источник данных
            org.maplibre.android.style.sources.GeoJsonSource debugSource = 
                new org.maplibre.android.style.sources.GeoJsonSource("debug-search-area", debugFeature);
            style.addSource(debugSource);
            
            // Создаем слой для отображения
            org.maplibre.android.style.layers.LineLayer debugLayer = 
                new org.maplibre.android.style.layers.LineLayer("debug-search-area", "debug-search-area");
            debugLayer.setProperties(
                org.maplibre.android.style.layers.PropertyFactory.lineColor(android.graphics.Color.RED),
                org.maplibre.android.style.layers.PropertyFactory.lineWidth(2f),
                org.maplibre.android.style.layers.PropertyFactory.lineOpacity(0.8f)
            );
            style.addLayer(debugLayer);
            
            Log.d(TAG, "updateDebugVisualization: отладочный квадрат добавлен на карту");
            
        } catch (Exception e) {
            Log.e(TAG, "updateDebugVisualization: ошибка при добавлении отладочной визуализации", e);
        }
    }
    
    /**
     * Включает/выключает режим отладки
     */
    public void setDebugMode(boolean enabled) {
        this.debugMode = enabled;
        if (!enabled && style != null && isStyleValid()) {
            // Удаляем отладочную визуализацию
            try {
                if (style.getLayer("debug-search-area") != null) {
                    style.removeLayer("debug-search-area");
                }
                if (style.getSource("debug-search-area") != null) {
                    style.removeSource("debug-search-area");
                }
                Log.d(TAG, "setDebugMode: отладочная визуализация удалена");
            } catch (Exception e) {
                Log.e(TAG, "setDebugMode: ошибка при удалении отладочной визуализации", e);
            }
        }
    }
    
    /**
     * Добавляет слой морских знаков OpenSeaMap
     */
    private void addSeamarksLayer() {
        if (style == null || !isStyleValid()) {
            Log.w(TAG, "addSeamarksLayer: стиль не готов");
            return;
        }
        
        try {
            // Проверяем, не добавлен ли уже слой
            if (style.getLayer(LAYER_SEAMARKS) != null) {
                Log.d(TAG, "addSeamarksLayer: слой уже существует");
                return;
            }
            
            // Создаем источник тайлов морских знаков через TileSet, чтобы шаблоны {z}/{x}/{y} обрабатывались корректно
            String[] seamarksUrls = {
                "http://t1.openseamap.org/seamark/{z}/{x}/{y}.png",
                "http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png"
            };

            boolean sourceAdded = false;
            for (String url : seamarksUrls) {
                try {
                    Log.d(TAG, "addSeamarksLayer: пробуем TileSet URL " + url);
                    org.maplibre.android.style.sources.TileSet tileSet =
                        new org.maplibre.android.style.sources.TileSet("2.1.0", url);
                    org.maplibre.android.style.sources.RasterSource seamarksSource =
                        new org.maplibre.android.style.sources.RasterSource(
                            SOURCE_SEAMARKS,
                            tileSet,
                            256
                        );
                    style.addSource(seamarksSource);
                    Log.d(TAG, "addSeamarksLayer: источник добавлен успешно через TileSet " + url);
                    sourceAdded = true;
                    break;
                } catch (Exception urlError) {
                    Log.w(TAG, "addSeamarksLayer: TileSet не удалось для URL " + url + ": " + urlError.getMessage());
                }
            }

            if (!sourceAdded) {
                Log.w(TAG, "addSeamarksLayer: ни один TileSet не подошел, пробуем информационный слой");
                createSeamarksInfoLayer();
                return;
            }
            
            // Создаем растровый слой
            org.maplibre.android.style.layers.RasterLayer seamarksLayer = 
                new org.maplibre.android.style.layers.RasterLayer(LAYER_SEAMARKS, SOURCE_SEAMARKS);
            
            // Настраиваем прозрачность слоя (чтобы не перекрывать основную карту)
            seamarksLayer.setProperties(
                org.maplibre.android.style.layers.PropertyFactory.rasterOpacity(0.8f)
            );
            
            // Добавляем слой поверх всех остальных слоев
            style.addLayer(seamarksLayer);
            Log.d(TAG, "addSeamarksLayer: слой добавлен успешно");
            
            Log.i(TAG, "✓ Слой морских знаков OpenSeaMap добавлен");
            
        } catch (Exception e) {
            Log.e(TAG, "addSeamarksLayer: ошибка добавления слоя морских знаков", e);
            // Пробуем альтернативный подход - используем второй официальный сервер
            try {
                Log.d(TAG, "addSeamarksLayer: пробуем альтернативный сервер tiles.openseamap.org");
                org.maplibre.android.style.sources.RasterSource fallbackSource = 
                    new org.maplibre.android.style.sources.RasterSource(
                        SOURCE_SEAMARKS + "_fallback",
                        "https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png",
                        256
                    );
                style.addSource(fallbackSource);
                
                org.maplibre.android.style.layers.RasterLayer fallbackLayer = 
                    new org.maplibre.android.style.layers.RasterLayer(LAYER_SEAMARKS, SOURCE_SEAMARKS + "_fallback");
                fallbackLayer.setProperties(
                    org.maplibre.android.style.layers.PropertyFactory.rasterOpacity(0.8f)
                );
                style.addLayer(fallbackLayer);
                
                Log.i(TAG, "✓ Слой морских знаков добавлен через fallback");
            } catch (Exception fallbackError) {
                Log.e(TAG, "addSeamarksLayer: fallback тоже не сработал", fallbackError);
            }
        }
    }
    
    /**
     * Создает информационный слой морских знаков (альтернатива растровым тайлам)
     */
    private void createSeamarksInfoLayer() {
        try {
            Log.d(TAG, "createSeamarksInfoLayer: создаем информационный слой");
            
            // Создаем простой источник с пустыми данными
            org.maplibre.android.style.sources.GeoJsonSource infoSource = 
                new org.maplibre.android.style.sources.GeoJsonSource(SOURCE_SEAMARKS, "{\"type\":\"FeatureCollection\",\"features\":[]}");
            
            style.addSource(infoSource);
            
            // Создаем слой символов для отображения информации
            org.maplibre.android.style.layers.SymbolLayer infoLayer = 
                new org.maplibre.android.style.layers.SymbolLayer(LAYER_SEAMARKS, SOURCE_SEAMARKS);
            
            infoLayer.setProperties(
                org.maplibre.android.style.layers.PropertyFactory.textField("⚓"),
                org.maplibre.android.style.layers.PropertyFactory.textSize(16f),
                org.maplibre.android.style.layers.PropertyFactory.textColor(android.graphics.Color.BLUE),
                org.maplibre.android.style.layers.PropertyFactory.textOpacity(0.7f)
            );
            
            style.addLayer(infoLayer);
            
            Log.i(TAG, "✓ Информационный слой морских знаков создан (альтернативный режим)");
            
        } catch (Exception e) {
            Log.e(TAG, "createSeamarksInfoLayer: ошибка создания информационного слоя", e);
        }
    }
    
    /**
     * Обновляет дополнительные слои карты на основе настроек
     */
    public void updateAdditionalLayers() {
        if (style == null || !isStyleValid()) {
            Log.w(TAG, "updateAdditionalLayers: стиль не готов");
            return;
        }
        
        try {
            // Обновляем слой морских знаков
            boolean seamarksEnabled = settingsManager.isSeamarksEnabled();
            boolean seamarksLayerExists = style.getLayer(LAYER_SEAMARKS) != null;
            
            if (seamarksEnabled && !seamarksLayerExists) {
                Log.i(TAG, "updateAdditionalLayers: включаем морские знаки");
                addSeamarksLayer();
            } else if (!seamarksEnabled && seamarksLayerExists) {
                Log.i(TAG, "updateAdditionalLayers: выключаем морские знаки");
                removeSeamarksLayer();
            }
            
        } catch (Exception e) {
            Log.e(TAG, "updateAdditionalLayers: ошибка обновления слоев", e);
        }
    }
    private void setupMapMovementListener() {
        if (maplibreMap != null) {
            maplibreMap.addOnCameraMoveListener(() -> {
                // Обновляем координаты курсора при движении карты
                updateCursorFromMapCenter();
            });
        }
    }
    
    /**
     * Удаляет слой морских знаков OpenSeaMap
     */
    private void removeSeamarksLayer() {
        if (style == null || !isStyleValid()) {
            Log.w(TAG, "removeSeamarksLayer: стиль не готов");
            return;
        }
        
        try {
            // Удаляем слой
            if (style.getLayer(LAYER_SEAMARKS) != null) {
                style.removeLayer(LAYER_SEAMARKS);
                Log.d(TAG, "removeSeamarksLayer: слой удален");
            }
            
            // Удаляем источник
            if (style.getSource(SOURCE_SEAMARKS) != null) {
                style.removeSource(SOURCE_SEAMARKS);
                Log.d(TAG, "removeSeamarksLayer: источник удален");
            }
            
            Log.i(TAG, "✓ Слой морских знаков OpenSeaMap удален");
            
        } catch (Exception e) {
            Log.e(TAG, "removeSeamarksLayer: ошибка удаления слоя морских знаков", e);
        }
    }
}
