package com.grigowashere.aismap.utils;

import com.grigowashere.aismap.models.AISVessel;
import com.grigowashere.aismap.models.Vessel;

/**
 * Утилиты для геодезических расчетов
 */
public class GeoUtils {
    
    private static final double EARTH_RADIUS = 6371000; // радиус Земли в метрах
    
    /**
     * Рассчитывает расстояние между двумя точками по формуле гаверсинуса
     * @param lat1 широта первой точки в градусах
     * @param lon1 долгота первой точки в градусах
     * @param lat2 широта второй точки в градусах
     * @param lon2 долгота второй точки в градусах
     * @return расстояние в метрах
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLat = Math.toRadians(lat2 - lat1);
        double deltaLon = Math.toRadians(lon2 - lon1);
        
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                   Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return EARTH_RADIUS * c;
    }
    
    /**
     * Рассчитывает расстояние от нашего судна до AIS судна
     * @param ourVessel наше судно
     * @param aisVessel AIS судно
     * @return расстояние в метрах
     */
    public static double calculateDistance(Vessel ourVessel, AISVessel aisVessel) {
        return calculateDistance(
            ourVessel.getLatitude(), ourVessel.getLongitude(),
            aisVessel.getLatitude(), aisVessel.getLongitude()
        );
    }
    
    /**
     * Рассчитывает пеленг от первой точки ко второй
     * @param lat1 широта первой точки в градусах
     * @param lon1 долгота первой точки в градусах
     * @param lat2 широта второй точки в градусах
     * @param lon2 долгота второй точки в градусах
     * @return пеленг в градусах (0-360)
     */
    public static double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLon = Math.toRadians(lon2 - lon1);
        
        double y = Math.sin(deltaLon) * Math.cos(lat2Rad);
        double x = Math.cos(lat1Rad) * Math.sin(lat2Rad) -
                   Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(deltaLon);
        
        double bearing = Math.toDegrees(Math.atan2(y, x));
        return (bearing + 360) % 360; // нормализуем к диапазону 0-360
    }
    
    /**
     * Рассчитывает пеленг от нашего судна до AIS судна
     * @param ourVessel наше судно
     * @param aisVessel AIS судно
     * @return пеленг в градусах (0-360)
     */
    public static double calculateBearing(Vessel ourVessel, AISVessel aisVessel) {
        return calculateBearing(
            ourVessel.getLatitude(), ourVessel.getLongitude(),
            aisVessel.getLatitude(), aisVessel.getLongitude()
        );
    }
    
    /**
     * Вычисляет относительный пеленг (сколько градусов влево/вправо от нашего курса)
     * @param ourCourse наш курс в градусах (0-360)
     * @param targetBearing пеленг до цели в градусах (0-360)
     * @return относительный пеленг в градусах (-180 до +180, отрицательное = влево, положительное = вправо)
     */
    public static double calculateRelativeBearing(double ourCourse, double targetBearing) {
        if (ourCourse < 0 || targetBearing < 0) return -1;
        
        double relativeBearing = targetBearing - ourCourse;
        
        // Нормализуем к диапазону -180 до +180
        while (relativeBearing > 180) relativeBearing -= 360;
        while (relativeBearing < -180) relativeBearing += 360;
        
        return relativeBearing;
    }
    
    /**
     * Проверяет валидность координат
     * @param latitude широта в градусах
     * @param longitude долгота в градусах
     * @return true если координаты валидны
     */
    public static boolean isValidCoordinates(double latitude, double longitude) {
        // Проверяем на нулевые координаты
        if (latitude == 0.0 && longitude == 0.0) {
            return false;
        }
        
        // Проверяем на невалидные координаты AIS (181, 91)
        if (latitude == 91.0 && longitude == 181.0) {
            return false;
        }
        
        // Проверяем на стандартные границы координат
        if (latitude < -90.0 || latitude > 90.0) {
            return false;
        }
        
        if (longitude < -180.0 || longitude > 180.0) {
            return false;
        }
        
        return true;
    }
    
    /**
     * Форматирует расстояние для отображения
     * @param distanceMeters расстояние в метрах
     * @return отформатированная строка
     */
    public static String formatDistance(double distanceMeters) {
        if (distanceMeters < 0) return "--";
        
        if (distanceMeters < 1000) {
            return String.format("%.0f м", distanceMeters);
        } else {
            return String.format("%.1f км", distanceMeters / 1000.0);
        }
    }
    
    /**
     * Форматирует относительный пеленг для отображения
     * @param relativeBearing относительный пеленг в градусах
     * @return отформатированная строка
     */
    public static String formatRelativeBearing(double relativeBearing) {
        // Проверяем на невалидные значения
        if (relativeBearing < -180 || relativeBearing > 180) return "--";
        
        if (Math.abs(relativeBearing) < 1) {
            return "прямо";
        } else if (relativeBearing > 0) {
            return String.format("%.0f° вправо", relativeBearing);
        } else {
            return String.format("%.0f° влево", Math.abs(relativeBearing));
        }
    }
    
    /**
     * Нормализует угол к диапазону 0-360 градусов
     * @param angle угол в градусах
     * @return нормализованный угол (0-360)
     */
    public static double normalizeAngle(double angle) {
        if (Double.isNaN(angle) || Double.isInfinite(angle)) return 0.0;
        double normalized = angle % 360.0;
        if (normalized < 0) normalized += 360.0;
        return normalized;
    }
    
    /**
     * Конвертирует навигационный статус в числовой код
     * @param navigationalStatus строковый статус
     * @return числовой код статуса
     */
    public static int getNavigationStatusCode(String navigationalStatus) {
        if (navigationalStatus == null) return -1;
        
        switch (navigationalStatus.toLowerCase()) {
            case "under way using engine":
            case "under way":
                return 0;
            case "at anchor":
            case "anchored":
                return 1;
            case "not under command":
                return 2;
            case "restricted manoeuvrability":
                return 3;
            case "constrained by her draught":
                return 4;
            case "moored":
                return 5;
            case "aground":
                return 6;
            case "engaged in fishing":
                return 7;
            case "under way sailing":
                return 8;
            default:
                return -1;
        }
    }
}
