package com.ruansee.macall.testwebview.utils;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.GnssStatus;
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Build;
import android.os.Bundle;

import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;

import com.rs.macall.androidx.basemodel.utils.LogUtils;
import com.ruansee.macall.testwebview.app.GPSLocationListener;
import com.ruansee.macall.testwebview.app.RuanseeApp;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * created by:   Macall
 * create time:  2023/3/28 9:30
 * copyright:    @ruansee.com
 * Describe:  封装了GPS定位工具类 该类实现了 LifecycleObserver 用于监听 Activity 和 Fragment 的生命周期
 * 避免因退出页面而忘记关闭定位导致应用耗时增加的问题
 * TODO @see #onDestroy(LifecycleOwner) 使用时请在调用者（Activity 或 Fragment ） 调用如下代码
 * TODO { getLifecycle().addObserver(locationGPSManage) }
 */
public class LocationGPSManage implements LifecycleObserver {
    private static LocationGPSManage locationGPSManage;
    private LocationManager locationManager;
    private boolean isLocation = false;
    private MyLocationListener locationListener;
    private MyGPSListener gpsListener;
    private GnssStatus.Callback gnssStatusCallback;

    /**
     * 记录上次搜索到的卫星数
     */
    private int startCount = -1;

    private List<GPSLocationListener> locationListenerList;

    /**
     * 该方法不需要手动调用
     * 宿主执行了onDestroy时 会分发该事件
     * 使用时请在宿主（Activity 或 Fragment ） 中调用如下代码，调用后当 宿主执行什么周期方法 onDestroy 该方法自动被调用
     * { getLifecycle().addObserver(locationGPSManage) }
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroy(@NotNull LifecycleOwner owner) {
        stopLocation();
    }

    private LocationGPSManage() {
    }

    /**
     * 添加一个位置监听
     *
     * @param gpsLocationListener 监听接口实例对象
     */
    public void addGPSLocationListener(@NotNull GPSLocationListener gpsLocationListener) {
        if (locationListenerList == null) {
            locationListenerList = new ArrayList<>();
        }
        if (locationListenerList.contains(gpsLocationListener)) return;
        locationListenerList.add(gpsLocationListener);
    }

    public static synchronized LocationGPSManage getInstance() {
        if (locationGPSManage == null) {
            synchronized (LocationGPSManage.class) {
                if (locationGPSManage == null) {
                    locationGPSManage = new LocationGPSManage();
                }
            }
        }
        return locationGPSManage;
    }

    /**
     * 开启定位
     *
     * @param context             上下文环境
     * @param gpsLocationListener 位置监听，也可以调用{@link #addGPSLocationListener(GPSLocationListener)}方法传递
     * @throws RuntimeException 要先获取定位权限，否则将抛出异常
     */
    @SuppressLint("MissingPermission")
    public void startGPSLocation(Context context, GPSLocationListener gpsLocationListener) throws RuntimeException {
        boolean gpsPermission = isGPSPermission(context);
        if (!gpsPermission)
            throw new RuntimeException("Not ACCESS_FINE_LOCATION AND ACCESS_COARSE_LOCATION Permission");
        if (gpsLocationListener != null) {
            addGPSLocationListener(gpsLocationListener);
        }
        //避免重复开启定位，该实例只允许开启一次定位
        if (isLocation) return;
        LogUtils.w("启动GPS定位");
        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        String bestProvider = locationManager.getBestProvider(getCriteria(), true);
        String currentProvider = "gps";
        if ("gps".equals(bestProvider)) {
            currentProvider = bestProvider;
        } else {
            //根据设置的Criteria对象，获取最符合此标准的provider对象
            LocationProvider provider = locationManager.getProvider(LocationManager.GPS_PROVIDER);
            if (provider != null) {
                currentProvider = provider.getName();
            }
        }
        LogUtils.w("获取的定位类型：" + currentProvider);
        //根据当前provider对象获取最后一次位置信息
        Location currentLocation = locationManager.getLastKnownLocation(currentProvider);
        if (locationListener == null) {
            locationListener = new MyLocationListener();
        }
        //如果位置信息不为NULL，则通知调用者最后一次位置
        if (currentLocation != null) {
            locationListener.onLocationChanged(currentLocation);
        }
        //根据系统版本确定使用哪个卫星搜索监听
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            if (gnssStatusCallback == null) {
                gnssStatusCallback = getGnssStatusCallback();
            }
            locationManager.registerGnssStatusCallback(gnssStatusCallback);
        } else {
            if (gpsListener == null) {
                gpsListener = new MyGPSListener();
            }
            locationManager.addGpsStatusListener(gpsListener);
        }
        locationManager.requestLocationUpdates(currentProvider, 5 * 1000, 3, locationListener);
        isLocation = true;
    }

    /**
     * 开启定位
     *
     * @param context 上下文环境
     * @throws RuntimeException 要先获取定位权限，否则将抛出异常
     */
    @SuppressLint("MissingPermission")
    public void startGPSLocation(Context context) throws RuntimeException {
        startGPSLocation(context, null);
    }

    private Criteria getCriteria() {
        Criteria criteria = new Criteria();
// 设置定位精确度 Criteria.ACCURACY_COARSE比较粗略，Criteria.ACCURACY_FINE则比较精细
        criteria.setAccuracy(Criteria.ACCURACY_COARSE);
// 设置是否要求速度
        criteria.setSpeedRequired(false);
// 设置是否允许运营商收费
        criteria.setCostAllowed(false);
// 设置是否需要方位信息
        criteria.setBearingRequired(true);
// 设置是否需要海拔信息
        criteria.setAltitudeRequired(true);
// 设置对电源的需求
        criteria.setPowerRequirement(Criteria.POWER_LOW);
        return criteria;
    }

    /**
     * 停止定位,无论 {@link #startGPSLocation(Context)} 被执行多少此，该方法被执行时将停止位置监听，其他以监听位置的活动也无法继续获取位置监听
     * 需要重新执行 startGPSLocation 才能继续监听位置
     * TODO 该方法被调用后 所有的 调用 {@link #addGPSLocationListener(GPSLocationListener)}该方法添加的监听都会被清空，再次开启定位是需要重新传递监听回调
     */
    public void stopLocation() {
        isLocation = false;
        LogUtils.w("停止GPS定位");
        if (locationListenerList != null) {
            for (GPSLocationListener l : locationListenerList) {
                if (l != null) {
                    l.stopLocation();
                }
            }
            removeGPSLocationListenerAll();
        }
        if (locationManager != null) {
            if (gpsListener != null) {
                locationManager.removeGpsStatusListener(gpsListener);
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                if (gnssStatusCallback != null) {
                    locationManager.unregisterGnssStatusCallback(gnssStatusCallback);
                }
            }
            if (locationListener != null) {
                locationManager.removeUpdates(locationListener);
            }
        }
        locationListener = null;
        gpsListener = null;
    }

    /**
     * 注销某个位置监听
     *
     * @param gpsLocationListener 监听实例对象
     */
    public void removeGPSLocationListener(GPSLocationListener gpsLocationListener) {
        if (gpsLocationListener == null) return;
        if (locationListenerList == null) return;
        locationListenerList.remove(gpsLocationListener);
    }

    /**
     * 注销所有位置监听
     */
    public void removeGPSLocationListenerAll() {
        if (locationListenerList == null) return;
        locationListenerList.clear();
    }

    @SuppressLint("MissingPermission")
    private class MyGPSListener implements GpsStatus.Listener {
        //GPS状态发生变化时触发
        @Override
        public void onGpsStatusChanged(int event) {
            //获取当前状态
            GpsStatus gpsstatus = locationManager.getGpsStatus(null);
            switch (event) {
                //第一次定位时的事件
                case GpsStatus.GPS_EVENT_FIRST_FIX:
                    break;
                //开始定位的事件
                case GpsStatus.GPS_EVENT_STARTED:
                    break;
                //发送GPS卫星状态事件
                case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
                    if (gpsstatus == null) return;
                    if (locationListenerList == null || locationListenerList.isEmpty()) return;
                    Iterable<GpsSatellite> allSatellites = gpsstatus.getSatellites();
                    if (allSatellites == null) return;
                    Iterator<GpsSatellite> it = allSatellites.iterator();
                    if (it == null) return;
                    int count = 0;
                    while (it.hasNext()) {
                        GpsSatellite next = it.next();
                        if (next.getSnr() != 0) {
                            count++;
                        }
                    }
//                    LogUtils.v("搜索到：" + count + "颗卫星！");
                    if (count == startCount) return;
                    for (GPSLocationListener l : locationListenerList) {
                        if (l != null) {
                            l.onScanSatellite(startCount, 0, 0);
                        }
                    }
                    break;
                //停止定位事件
                case GpsStatus.GPS_EVENT_STOPPED:
                    LogUtils.w("Location", "GPS_EVENT_STOPPED");
                    break;
            }
        }
    }


    @RequiresApi(api = Build.VERSION_CODES.N)
    private GnssStatus.Callback getGnssStatusCallback() {
        return new GnssStatus.Callback() {
            @Override
            public void onStarted() {
                super.onStarted();
            }

            @Override
            public void onSatelliteStatusChanged(GnssStatus status) {
                if (locationListenerList == null || locationListenerList.isEmpty()) return;
                int satelliteCount = status.getSatelliteCount();
                if (startCount != satelliteCount) {
                    startCount = satelliteCount;
                    //计算北东卫星数量
                    int bdCount = 0;
                    //计算用于定位的卫星数量
                    int locationCount = 0;
                    for (int i = 0; i < satelliteCount; i++) {
                        int constellationType = status.getConstellationType(i);
                        if (constellationType == GnssStatus.CONSTELLATION_BEIDOU) {
                            bdCount++;
                        }
                        if (status.usedInFix(i)) {
                            locationCount++;
                        }
                    }
                    for (GPSLocationListener l : locationListenerList) {
                        if (l != null) {
                            l.onScanSatellite(startCount, bdCount, locationCount);
                        }
                    }
                    if (RuanseeApp.isPrintLog) {
                        LogUtils.d("搜索卫星", String.format("搜索到：%s颗卫星，北斗卫星：%s颗，用于定位的卫星：%s颗", startCount, bdCount, locationCount));
                    }
                }
            }
        };
    }

    public boolean isGPSPermission(Context context) {
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            return false;
        }
        return true;
    }

    private class MyLocationListener implements LocationListener {

        /**
         * 位置发生改变
         *
         * @param location
         */
        @Override
        public void onLocationChanged(Location location) {
            if (locationListenerList == null || locationListenerList.isEmpty()) return;
            for (GPSLocationListener l : locationListenerList) {
                if (l != null) {
                    l.onLocationChanged(location);
                }
            }
        }

        /**
         * 定位状态发生改变
         *
         * @param provider
         * @param status
         * @param extras
         */
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        /**
         * 启用 定位模式
         *
         * @param provider
         */
        @Override
        public void onProviderEnabled(String provider) {

        }

        /**
         * 定位模式 被禁用
         *
         * @param provider
         */
        @Override
        public void onProviderDisabled(String provider) {

        }
    }

}
