package com.qaup.common.utils;

import com.qaup.common.constant.GeoConstants;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 地理计算工具类
 * 提供统一的地理距离、方向、速度计算方法
 * 
 * @author qaup
 */
public class GeoUtils {
    
    /**
     * 使用Haversine公式计算两点间的地理距离
     * 
     * @param lat1 第一个点的纬度
     * @param lon1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lon2 第二个点的经度
     * @return 距离（米）
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        if (lat1 == lat2 && lon1 == lon2) {
            return 0.0;
        }
        
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLatRad = Math.toRadians(lat2 - lat1);
        double deltaLonRad = Math.toRadians(lon2 - lon1);
        
        double a = Math.sin(deltaLatRad / 2) * Math.sin(deltaLatRad / 2) +
                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
                   Math.sin(deltaLonRad / 2) * Math.sin(deltaLonRad / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return GeoConstants.EARTH_RADIUS_METERS * c;
    }
    
    /**
     * 使用Haversine公式计算两点间的地理距离，返回BigDecimal
     * 
     * @param lat1 第一个点的纬度
     * @param lon1 第一个点的经度
     * @param lat2 第二个点的纬度
     * @param lon2 第二个点的经度
     * @return 距离（米），保留指定精度
     */
    public static BigDecimal calculateDistanceAsBigDecimal(double lat1, double lon1, double lat2, double lon2) {
        double distance = calculateDistance(lat1, lon1, lat2, lon2);
        return BigDecimal.valueOf(distance).setScale(GeoConstants.DISTANCE_PRECISION, RoundingMode.HALF_UP);
    }
    
    /**
     * 计算两点间的方向角（方位角）
     * 
     * @param lat1 起点纬度
     * @param lon1 起点经度
     * @param lat2 终点纬度
     * @param lon2 终点经度
     * @return 方向角（度，0-360度，正北为0度）
     */
    public static double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLonRad = Math.toRadians(lon2 - lon1);
        
        double y = Math.sin(deltaLonRad) * Math.cos(lat2Rad);
        double x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - 
                   Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(deltaLonRad);
        
        double bearingRad = Math.atan2(y, x);
        double bearingDeg = Math.toDegrees(bearingRad);
        
        // 转换为0-360度范围
        return (bearingDeg + 360) % 360;
    }
    
    /**
     * 根据距离和时间计算速度
     * 
     * @param distanceMeters 距离（米）
     * @param timeSeconds 时间（秒）
     * @return 速度（公里/小时）
     */
    public static double calculateSpeed(double distanceMeters, double timeSeconds) {
        if (timeSeconds <= 0) {
            return 0.0;
        }
        
        double speedMps = distanceMeters / timeSeconds;
        return speedMps * GeoConstants.MPS_TO_KPH;
    }
    
    /**
     * 根据距离和时间计算速度，返回指定精度
     * 
     * @param distanceMeters 距离（米）
     * @param timeSeconds 时间（秒）
     * @return 速度（公里/小时），保留指定精度
     */
    public static double calculateSpeedWithPrecision(double distanceMeters, double timeSeconds) {
        double speed = calculateSpeed(distanceMeters, timeSeconds);
        return Math.round(speed * Math.pow(10, GeoConstants.SPEED_PRECISION)) / Math.pow(10, GeoConstants.SPEED_PRECISION);
    }
    
    /**
     * 度到米的近似转换（适用于小范围计算）
     * 
     * @param degrees 度数
     * @return 米数（近似值）
     */
    public static double degreesToMeters(double degrees) {
        return degrees * GeoConstants.DEGREE_TO_METER;
    }
    
    /**
     * 米到度的近似转换（适用于小范围计算）
     * 
     * @param meters 米数
     * @return 度数（近似值）
     */
    public static double metersToDegrees(double meters) {
        return meters * GeoConstants.METER_TO_DEGREE;
    }
    
    /**
     * 米每秒转换为公里每小时
     * 
     * @param mps 米每秒
     * @return 公里每小时
     */
    public static double mpsToKph(double mps) {
        return mps * GeoConstants.MPS_TO_KPH;
    }
    
    /**
     * 公里每小时转换为米每秒
     * 
     * @param kph 公里每小时
     * @return 米每秒
     */
    public static double kphToMps(double kph) {
        return kph * GeoConstants.KPH_TO_MPS;
    }
    
    /**
     * 格式化坐标到指定精度
     * 
     * @param coordinate 坐标值
     * @param precision 精度（小数位数）
     * @return 格式化后的坐标字符串
     */
    public static String formatCoordinate(double coordinate, int precision) {
        return String.format("%." + precision + "f", coordinate);
    }
    
    /**
     * 格式化坐标到默认精度
     * 
     * @param coordinate 坐标值
     * @return 格式化后的坐标字符串
     */
    public static String formatCoordinate(double coordinate) {
        return formatCoordinate(coordinate, GeoConstants.DEFAULT_COORDINATE_PRECISION);
    }
    
    /**
     * 验证经度是否有效
     * 
     * @param longitude 经度
     * @return 是否有效
     */
    public static boolean isValidLongitude(double longitude) {
        return longitude >= -180.0 && longitude <= 180.0;
    }
    
    /**
     * 验证纬度是否有效
     * 
     * @param latitude 纬度
     * @return 是否有效
     */
    public static boolean isValidLatitude(double latitude) {
        return latitude >= -90.0 && latitude <= 90.0;
    }
    
    /**
     * 验证坐标是否有效
     * 
     * @param longitude 经度
     * @param latitude 纬度
     * @return 是否有效
     */
    public static boolean isValidCoordinate(double longitude, double latitude) {
        return isValidLongitude(longitude) && isValidLatitude(latitude);
    }
}
