package com.grigowashere.aismap.maps;

import android.graphics.Color;
import com.yandex.mapkit.geometry.Point;
import com.yandex.mapkit.map.MapObjectCollection;
import com.yandex.mapkit.map.PolylineMapObject;
import com.yandex.mapkit.map.MapObject;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * Класс для отслеживания и отображения пути движения судна
 * Отображает сплошную линию пройденного пути и прогнозируемое движение
 */
public class VesselPathTracker {
    
    private static final int MAX_PATH_POINTS = 100; // Максимальное количество точек в пути
    private static final long MIN_TIME_BETWEEN_POINTS = 1000; // Минимальное время между точками (1 секунда)
    private static final double MIN_DISTANCE_BETWEEN_POINTS = 10.0; // Минимальное расстояние между точками (10 метров)
    
    private String vesselId;
    private MapObjectCollection mapObjects;
    private ConcurrentLinkedQueue<PathPoint> pathHistory;
    private PolylineMapObject pathLine;
    private PolylineMapObject predictionLine;
    private long lastUpdateTime;
    private Point lastPosition;
    
    
    // Настройки отображения
    private int pathColor = Color.CYAN;
    private int predictionColor = Color.YELLOW;
    private float pathWidth = 3.0f;
    private float predictionWidth = 2.0f;
    private boolean isEnabled = true;
    
    /**
     * Точка пути с временной меткой
     */
    private static class PathPoint {
        public final Point position;
        public final long timestamp;
        public final double speed;
        public final double course;
        
        public PathPoint(Point position, long timestamp, double speed, double course) {
            this.position = position;
            this.timestamp = timestamp;
            this.speed = speed;
            this.course = course;
        }
    }
    
    public VesselPathTracker(String vesselId, MapObjectCollection mapObjects) {
        this.vesselId = vesselId;
        this.mapObjects = mapObjects;
        this.pathHistory = new ConcurrentLinkedQueue<>();
        this.lastUpdateTime = 0;
    }
    
    /**
     * Обновляет путь судна новой позицией
     */
    public void updatePosition(double latitude, double longitude, double speed, double course) {
        if (!isEnabled) {
            return;
        }
        
        long currentTime = System.currentTimeMillis();
        Point newPosition = new Point(latitude, longitude);
        
        // Проверяем, нужно ли добавить новую точку
        if (shouldAddPoint(newPosition, currentTime)) {
            PathPoint newPoint = new PathPoint(newPosition, currentTime, speed, course);
            pathHistory.offer(newPoint);
            
            // Ограничиваем количество точек
            while (pathHistory.size() > MAX_PATH_POINTS) {
                pathHistory.poll();
            }
            
            lastPosition = newPosition;
            lastUpdateTime = currentTime;
            
            // Обновляем отображение пути
            updatePathDisplay();
        }
    }
    
    /**
     * Проверяет, нужно ли добавить новую точку в путь
     */
    private boolean shouldAddPoint(Point newPosition, long currentTime) {
        // Проверяем время
        if (currentTime - lastUpdateTime < MIN_TIME_BETWEEN_POINTS) {
            return false;
        }
        
        // Проверяем расстояние
        if (lastPosition != null) {
            double distance = calculateDistance(lastPosition, newPosition);
            if (distance < MIN_DISTANCE_BETWEEN_POINTS) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Обновляет отображение пути на карте
     */
    private void updatePathDisplay() {
        if (pathHistory.isEmpty()) {
            return;
        }
        if (mapObjects == null) {
            return;
        }
        
        // Создаем список точек для пройденного пути
        List<Point> pathPoints = new ArrayList<>();
        for (PathPoint point : pathHistory) {
            pathPoints.add(point.position);
        }
        
        // Удаляем старые линии
        try {
            if (pathLine != null) {
                mapObjects.remove(pathLine);
                pathLine = null;
            }
            if (predictionLine != null) {
                mapObjects.remove(predictionLine);
                predictionLine = null;
            }
        } catch (RuntimeException ignored) {
            // Коллекция могла быть инвалидирована (weak_ptr expired). Прекращаем обновления.
            isEnabled = false;
            return;
        }
        
        // Создаем линию пройденного пути
        if (pathPoints.size() > 1) {
            try {
                pathLine = mapObjects.addPolyline(new com.yandex.mapkit.geometry.Polyline(pathPoints));
                if (pathLine != null) {
                    pathLine.setStrokeColor(pathColor);
                    pathLine.setStrokeWidth(pathWidth);
                }
            } catch (RuntimeException ignored) {
                isEnabled = false;
                return;
            }
        }
        
        // Создаем линию прогнозируемого движения
        createPredictionLine();
    }
    
    /**
     * Создает линию прогнозируемого движения
     */
    private void createPredictionLine() {
        if (pathHistory.isEmpty()) {
            return;
        }
        if (mapObjects == null) {
            return;
        }
        
        // Получаем последнюю точку
        PathPoint lastPoint = null;
        for (PathPoint point : pathHistory) {
            lastPoint = point;
        }
        
        if (lastPoint == null || lastPoint.speed <= 0) {
            return;
        }
        
        // Рассчитываем прогнозируемую позицию через 1 минуту
        double predictionTimeMinutes = 1.0; // 1 минута
        double predictionDistance = lastPoint.speed * predictionTimeMinutes * 60.0; // расстояние в метрах
        
        // Конвертируем курс в радианы
        double courseRad = Math.toRadians(lastPoint.course);
        
        // Рассчитываем новую позицию
        double earthRadius = 6371000; // радиус Земли в метрах
        double lat1 = Math.toRadians(lastPoint.position.getLatitude());
        double lon1 = Math.toRadians(lastPoint.position.getLongitude());
        
        double lat2 = Math.asin(Math.sin(lat1) * Math.cos(predictionDistance / earthRadius) +
                               Math.cos(lat1) * Math.sin(predictionDistance / earthRadius) * Math.cos(courseRad));
        
        double lon2 = lon1 + Math.atan2(Math.sin(courseRad) * Math.sin(predictionDistance / earthRadius) * Math.cos(lat1),
                                       Math.cos(predictionDistance / earthRadius) - Math.sin(lat1) * Math.sin(lat2));
        
        Point predictionPoint = new Point(Math.toDegrees(lat2), Math.toDegrees(lon2));
        
        // Создаем линию прогноза
        List<Point> predictionPoints = new ArrayList<>();
        predictionPoints.add(lastPoint.position);
        predictionPoints.add(predictionPoint);
        
        try {
            predictionLine = mapObjects.addPolyline(new com.yandex.mapkit.geometry.Polyline(predictionPoints));
            if (predictionLine != null) {
                predictionLine.setStrokeColor(predictionColor);
                predictionLine.setStrokeWidth(predictionWidth);
                // Сплошная линия для прогноза (по умолчанию)
            }
        } catch (RuntimeException ignored) {
            isEnabled = false;
        }
    }
    
    /**
     * Рассчитывает расстояние между двумя точками в метрах
     */
    private double calculateDistance(Point point1, Point point2) {
        double lat1 = Math.toRadians(point1.getLatitude());
        double lon1 = Math.toRadians(point1.getLongitude());
        double lat2 = Math.toRadians(point2.getLatitude());
        double lon2 = Math.toRadians(point2.getLongitude());
        
        double dlat = lat2 - lat1;
        double dlon = lon2 - lon1;
        
        double a = Math.sin(dlat / 2) * Math.sin(dlat / 2) +
                   Math.cos(lat1) * Math.cos(lat2) * Math.sin(dlon / 2) * Math.sin(dlon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return 6371000 * c; // радиус Земли в метрах
    }
    
    /**
     * Очищает путь судна
     */
    public void clearPath() {
        try {
            if (pathLine != null && mapObjects != null) {
                mapObjects.remove(pathLine);
                pathLine = null;
            }
            if (predictionLine != null && mapObjects != null) {
                mapObjects.remove(predictionLine);
                predictionLine = null;
            }
        } catch (RuntimeException ignored) {
            // Игнорируем ошибки очистки при невалидной коллекции
        }
        
        
        pathHistory.clear();
        lastPosition = null;
    }
    
    /**
     * Удаляет трекер пути
     */
    public void remove() {
        clearPath();
    }
    
    /**
     * Включает/выключает отображение пути
     */
    public void setEnabled(boolean enabled) {
        this.isEnabled = enabled;
        if (!enabled) {
            clearPath();
        }
    }
    
    /**
     * Устанавливает цвет пройденного пути
     */
    public void setPathColor(int color) {
        this.pathColor = color;
        if (pathLine != null) {
            pathLine.setStrokeColor(color);
        }
        
    }
    
    /**
     * Устанавливает цвет прогнозируемого пути
     */
    public void setPredictionColor(int color) {
        this.predictionColor = color;
        if (predictionLine != null) {
            predictionLine.setStrokeColor(color);
        }
    }
    
    /**
     * Устанавливает ширину линий
     */
    public void setLineWidth(float pathWidth, float predictionWidth) {
        this.pathWidth = pathWidth;
        this.predictionWidth = predictionWidth;
        
        if (pathLine != null) {
            pathLine.setStrokeWidth(pathWidth);
        }
        if (predictionLine != null) {
            predictionLine.setStrokeWidth(predictionWidth);
        }
        
    }
    
    /**
     * Проверяет, активен ли трекер
     */
    public boolean isActive() {
        return isEnabled && !pathHistory.isEmpty();
    }
    
    /**
     * Получает количество точек в пути
     */
    public int getPathPointCount() {
        return pathHistory.size();
    }
}
