package com.nsgk.ruralWeb.location;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;

import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.location.CoordinateConverter;
import com.amap.api.location.DPoint;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

// 主线程调用, 无线程安全
public class NSAMapLocation implements AMapLocationListener
{
    // 定位场景: 默认无场景
    public static final int FLAG_PURPOSE_SIGNIN    = 1; // 签到
    public static final int FLAG_PURPOSE_TRANSPORT = 1 << 1; // 出行
    public static final int FLAG_PURPOSE_SPORT     = 1 << 2; // 运动

    // 定位模式
    public static final int FLAG_MODE_HIGH    = 1 << 3; // 高精度模式
    public static final int FLAG_MODE_SAVING  = 1 << 4; // 低功耗模式
    public static final int FLAG_MODE_SENSORS = 1 << 5; // 仅设备模式

    // 获取一次定位结果: 默认为false
    public static final int FLAG_ONCE = 1 << 6; // true

    // 获取最近3s内精度最高的一次定位结果: 默认为false
    public static final int FLAG_ONCE_LATEST = 1 << 7; // true

    // 设置是否返回地址信息: 默认返回地址信息
    public static final int FLAG_NO_NEED_ADDRESS = 1 << 8; // false

    // 设置是否允许模拟位置: 默认为true
    public static final int FLAG_MOCK_DISABLE = 1 << 9; // false

    // 关闭缓存机制: 默认缓存
    public static final int FLAG_CACHE_DISABLE = 1 << 10; // false

    // 线程
    public static final int FLAG_BUILTIN_THREAD = 1 << 11; // 使用内建线程
    public static final int FLAG_CUSTOM_THREAD  = 1 << 12; // 使用外部线程
    public static final int FLAG_MAIN_THREAD    = 1 << 13; // 使用主线程

    public static final int DEFAULT_FLAG = FLAG_MODE_HIGH | FLAG_NO_NEED_ADDRESS;


    public static final int OPTION_INTERVAL     = 1; // 定位间隔(毫秒) 默认1000
    public static final int OPTION_HTTP_TIMEOUT = 2; // 定位间隔(毫秒) 默认20000

    private static final int DEFAULT_INTERVAL = 1000;
    private static final int DEFAULT_HTTP_TIMEOUT = 20000;

    private static final String ID_TAG = NSSystemLocation.class.getName();

    private final Object m_lock = new Object();

    //声明AMapLocationClient类对象
    public        AMapLocationClient       mLocationClient   = null;
    //声明定位回调监听器
    public        AMapLocationListener     mLocationListener = this;
    //声明AMapLocationClientOption对象
    public        AMapLocationClientOption mLocationOption   = null;
    private final Context                  m_context;
    private       int                      m_flag            = DEFAULT_FLAG;
    private boolean        m_isPreInit    = false;
    private NSLocationInfo m_lastLocation = null;
    private HandlerThread  m_thread       = null;
    private       Looper                   m_customLooper    = null;
    private       AtomicInteger            m_readOnce        = new AtomicInteger(-1);
    private       Map<Integer, Object>     m_options         = new HashMap<>();

    public NSAMapLocation(Context context)
    {
        m_context = context;
    }

    public boolean Init(int flag)
    {
        if(IsInitialized())
        {
            Log.e(ID_TAG, "定位已初始化");
            return true;
        }

        try
        {
            m_readOnce.set(-1);
            m_flag = flag < 0 ? DEFAULT_FLAG : flag;

            // 在构造AMapLocationClient 之前必须进行合规检查，设置接口之前保证隐私政策合规
            PreInit();

            Context applicationContext = m_context.getApplicationContext();
            //初始化定位
            if(HasFlag(FLAG_BUILTIN_THREAD))
            {
                m_thread = new HandlerThread("AMap内建定位");
                m_thread.start();
                mLocationClient = new AMapLocationClient(m_thread.getLooper(), applicationContext);
                Log.d(ID_TAG, "定位使用内建线程: " + m_thread.getId());
            }
            else if(HasFlag(FLAG_CUSTOM_THREAD))
            {
                if(null == m_customLooper)
                {
                    throw new RuntimeException("请先传入线程Looper");
                }
                mLocationClient = new AMapLocationClient(m_customLooper, applicationContext);
                Log.d(ID_TAG, "定位使用用户指定线程: " + m_customLooper.getThread().getId());
            }
            else if(HasFlag(FLAG_MAIN_THREAD))
            {
                Looper mainLooper = Looper.getMainLooper();
                mLocationClient = new AMapLocationClient(mainLooper, applicationContext);
                Log.d(ID_TAG, "定位使用主线程: " + mainLooper.getThread().getId());
            }
            else
            {
                mLocationClient = new AMapLocationClient(applicationContext);
                Log.d(ID_TAG, "定位使用当前线程: " + Thread.currentThread().getId());
            }

            //设置定位回调监听
            mLocationClient.setLocationListener(mLocationListener);

            InitOptions();

            mLocationClient.setLocationOption(mLocationOption);

            //V6.4.9版本起 client设置逆地理回调
/*            mLocationClient.setReGeoLocationCallback(new IReGeoLocationCallback()
            {
                @Override
                public void onReGeoLocation(AMapLocation reGeoLocation)
                {
                    Log.i(ID_TAG, reGeoLocation.toStr());
                }
            });*/

            Log.i(ID_TAG, "定位初始化完成");
            return true;
        }
        catch(Exception e)
        {
            e.printStackTrace();
            mLocationClient = null;
            return false;
        }
    }

    public void SetThread(Looper looper)
    {
        CheckInitialization(false);
        m_customLooper = looper;
    }

    public void SetThread(HandlerThread thread)
    {
        CheckInitialization(false);
        m_customLooper = thread.getLooper();
    }

    public boolean IsRunning()
    {
        return IsInitialized() && mLocationClient.isStarted();
    }

    public void Start()
    {
        Run(-1);
    }

    public void Stop()
    {
        CheckInitialization(true);

        //设置场景模式后最好调用一次stop，再调用start以保证场景模式生效
        mLocationClient.stopLocation();

        Log.i(ID_TAG, "定位停止");
    }

    public void Shutdown()
    {
        if(!IsInitialized())
        {
            Log.e(ID_TAG, "请先初始化");
            return;
        }

        Stop();

        mLocationClient.onDestroy();
        mLocationClient = null;
        if(null != m_thread)
        {
            m_thread.quit();
            m_thread = null;
            Log.d(ID_TAG, "内建线程结束");
        }

        Log.i(ID_TAG, "定位销毁");
    }

    public void PreInit()
    {
        if(m_isPreInit)
            return;
        Context applicationContext = m_context.getApplicationContext();
        /** 设置包含隐私政策，并展示用户授权弹窗 <b>必须在AmapLocationClient实例化之前调用</b>
         *
         * @param context
         * @param isContains: 是隐私权政策是否包含高德开平隐私权政策  true是包含
         * @param isShow: 隐私权政策是否弹窗展示告知用户 true是展示
         * @since 5.6.0
         */
        AMapLocationClient.updatePrivacyShow(applicationContext, true, true);
        /**
         * 设置是否同意用户授权政策 <b>必须在AmapLocationClient实例化之前调用</b>
         * @param context
         * @param isAgree:隐私权政策是否取得用户同意  true是用户同意
         *
         * @since 5.6.0
         */
        AMapLocationClient.updatePrivacyAgree(applicationContext, true);
        Log.i(ID_TAG, "定位预初始化");
        m_isPreInit = true;
    }

    public boolean IsInitialized()
    {
        return null != mLocationClient;
    }

    private void CheckInitialization(boolean inited)
    {
        if(IsInitialized() != inited)
        {
            String msg = inited ? "定位未初始化" : "定位已初始化";
            Log.e(ID_TAG, msg);
            throw new RuntimeException(msg);
        }
    }

    private boolean HasFlag(int f)
    {
        return (m_flag & f) != 0;
    }

    public void SetOption(int type, Object value)
    {
        CheckInitialization(false);
        m_options.put(type, value);
    }

    @SuppressLint("unchecked")
    private <T> T GetOption(int type, T def)
    {
        Object o = m_options.get(type);
        if(null == o)
            return def;
        return (T) o;
    }

    private void InitOptions()
    {
        //初始化AMapLocationClientOption对象
        mLocationOption = new AMapLocationClientOption();

        /**
         * 设置定位场景，目前支持三种场景（签到、出行、运动，默认无场景）
         */
        if(HasFlag(FLAG_PURPOSE_SIGNIN))
            mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.SignIn);
        else if(HasFlag(FLAG_PURPOSE_TRANSPORT))
            mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Transport);
        else if(HasFlag(FLAG_PURPOSE_SPORT))
            mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);

        //设置定位模式为AMapLocationMode.Hight_Accuracy，高精度模式。
        if(HasFlag(FLAG_MODE_HIGH))
            mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
            //设置定位模式为AMapLocationMode.Battery_Saving，低功耗模式。
        else if(HasFlag(FLAG_MODE_SAVING))
            mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Battery_Saving);
            //设置定位模式为AMapLocationMode.Device_Sensors，仅设备模式。
        else if(HasFlag(FLAG_MODE_SENSORS))
            mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);

        //获取一次定位结果：
        //该方法默认为false。
        if(HasFlag(FLAG_ONCE))
            mLocationOption.setOnceLocation(true);

        //获取最近3s内精度最高的一次定位结果：
        //设置setOnceLocationLatest(boolean b)接口为true，启动定位时SDK会返回最近3s内精度最高的一次定位结果。如果设置其为true，setOnceLocation(boolean b)接口也会被设置为true，反之不会，默认为false。
        if(HasFlag(FLAG_ONCE_LATEST))
            mLocationOption.setOnceLocationLatest(true);

        //设置定位间隔,单位毫秒,默认为2000ms，最低1000ms。
        int interval = GetOption(OPTION_INTERVAL, DEFAULT_INTERVAL);
        mLocationOption.setInterval(interval);

        //设置是否返回地址信息（默认返回地址信息）
        if(HasFlag(FLAG_NO_NEED_ADDRESS))
            mLocationOption.setNeedAddress(false);

        //设置是否允许模拟位置,默认为true，允许模拟位置
        if(HasFlag(FLAG_MOCK_DISABLE))
            mLocationOption.setMockEnable(false);

        //单位是毫秒，默认30000毫秒，建议超时时间不要低于8000毫秒。
        int timeout = GetOption(OPTION_HTTP_TIMEOUT, DEFAULT_HTTP_TIMEOUT);
        mLocationOption.setHttpTimeOut(timeout);

        //关闭缓存机制
        if(HasFlag(FLAG_CACHE_DISABLE))
            mLocationOption.setLocationCacheEnable(false);
    }

    private void Run(int num)
    {
        CheckInitialization(true);

        m_readOnce.set(num);

        //设置场景模式后最好调用一次stop，再调用start以保证场景模式生效
        mLocationClient.stopLocation();
        mLocationClient.startLocation();

        Log.i(ID_TAG, "定位开始");
    }

    /*
    在调用线程中执行
     */
    @Override
    public void onLocationChanged(AMapLocation amapLocation)
    {
        Log.d(ID_TAG, "onLocationChanged线程: " + Thread.currentThread().getId());
        System.err.println(amapLocation);
        SetLastLocation(amapLocation);
        if(null != amapLocation)
        {
            if(m_readOnce.get() > 0)
            {
                m_readOnce.decrementAndGet();
                if(m_readOnce.get() == 0)
                {
                    synchronized(m_lock)
                    {
                        m_lock.notifyAll();
                    }
                }
            }
        }
    }

    private double[] Convert(double longitude, double latitude)
    {
        try
        {
            //初始化坐标转换类
            CoordinateConverter converter = new CoordinateConverter(m_context.getApplicationContext());
            converter.from(CoordinateConverter.CoordType.GPS);
            //设置需要转换的坐标
            converter.coord(new DPoint(latitude, longitude));
            //转换成高德坐标
            DPoint destPoint = converter.convert();
            double dx = destPoint.getLongitude() - longitude;
            double dy = destPoint.getLatitude() - latitude;
            longitude = longitude - dx;
            latitude = latitude - dy;
            return new double[]{
                    longitude, latitude
            };
        }
        catch(Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    private void SetLastLocation(AMapLocation amapLocation)
    {
        if(null != amapLocation)
        {
            double longitude = amapLocation.getLongitude();
            double latitude = amapLocation.getLatitude();
            double[] coord = Convert(longitude, latitude);
            longitude = coord[0];
            latitude = coord[1];
            m_lastLocation = new NSLocationInfo(amapLocation.getProvider(), longitude, latitude);
        }
        else
        {
            m_lastLocation = null;
        }
    }

    /**
     * 同步调用, 会锁住线程
     */
    public NSLocationInfo Read(int count)
    {
        CheckInitialization(true);
/*        if(IsRunning())
        {
            throw new RuntimeException("请先停止定位");
        }*/

        Log.d(ID_TAG, "定位线程: " + Thread.currentThread().getId());
        synchronized(m_lock)
        {
            try
            {
                CleanLastLocation();
                Run(count);
                m_lock.wait();
            }
            catch(Exception e)
            {
                throw new RuntimeException(e);
            }
            finally
            {
                Stop();
            }
        }
        return m_lastLocation;
    }

    public NSLocationInfo GetLastLocation()
    {
        return m_lastLocation;
    }

    public void CleanLastLocation()
    {
        m_lastLocation = null;
    }
}
