//
//  WMLocationManager.m
//  Gengmei
//
//  Created by wangyang on 5/6/15.
//  Copyright (c) 2015 Wanmeichuangyi. All rights reserved.
//

#import "GMLocationManager.h"
#import "JZLocationConverter.h"
@import GMCache;

@interface GMLocationManager (){
    NSMutableArray<LocationFinishBlock> *_blocks;
    // 因为定位回调会走多次，使用该变量可以设定为只使用第一次返回的数据
    BOOL _firstTime;
}

@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CLLocation *location;
@property (nonatomic, assign) NSInteger statusWhenAppBackground;

@end

@implementation GMLocationManager
+ (instancetype)shareInstance
{
    static dispatch_once_t onceToken;
    static id instance = nil;
    dispatch_once(&onceToken, ^{
        instance = [[[self class] alloc] init];
    });

    return instance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {

        _blocks = [NSMutableArray array];

        // 实例化一个位置管理器
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
        // 设置定位精度
        // kCLLocationAccuracyNearestTenMeters:精度10米
        // kCLLocationAccuracyHundredMeters:精度100 米
        // kCLLocationAccuracyKilometer:精度1000 米
        // kCLLocationAccuracyThreeKilometers:精度3000米
        // kCLLocationAccuracyBest:设备使用电池供电时候最高的精度
        // kCLLocationAccuracyBestForNavigation:导航情况下最高精度，一般要有外接电源时才能使用
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
        // distanceFilter是距离过滤器，为了减少对定位装置的轮询次数，位置的改变不会每次都去通知委托，而是在移动了足够的距离时才通知委托程序
        // 它的单位是米，这里设置为至少移动1000再通知委托处理更新;
        self.locationManager.distanceFilter = 1000.0f;// 如果设为kCLDistanceFilterNone，则每秒更新一次;

        // 初始化
        _location = [[CLLocation alloc] initWithLatitude:0 longitude:0];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];

        _firstTime = YES;
        if ([GMLocationManager hasOpenLocationService]) {
            [self updateLocation];
        }
    }
    return self;
}

- (void)applicationDidEnterBackground {
    self.statusWhenAppBackground = [CLLocationManager authorizationStatus];
}


#pragma mark - Method

/**
 *  @author wangyang, 16-03-10 12:03:57
 *
 *  @brief 会判断是否允许定位服务
 *  @return YES表示允许
 *  @since 5.9.2
 */
+ (BOOL)hasOpenLocationService {
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if(status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse){
        return YES;
    }else{
        return NO;
    }
}

/**
 *  @author wangyang, 16-02-24 15:02:11
 *
 *  @brief 尝试使用定位功能。如果没有权限会有提示，如果有权限直接重新定位
 *  @since 5.9.1
 */
- (void)tryLocationService {
    [GMLocationManager showAlertIfNotAllowed];
    [self requestAuthorization];
}

- (void)tryLocationServiceForKey:(NSString *)key {
    [GMLocationManager showAlertIfNotAllowedForKey:key];
    [self requestAuthorization];
}

+ (void)showAlertIfNotAllowed {
    [self showAlertIfNotAllowedForKey:nil];
}

/**
 *  @author wangyang, 16-03-10 12:03:21
 *
 *  @brief 会判断是否允许定位服务，如果不允许会弹出alert
 *  @since 5.9.2
 */
+ (void)showAlertIfNotAllowedForKey:(NSString *)key {

    if (key != nil) {
        NSString *value = [GMCache fetchObjectAtDocumentPathWithkey:key];
        if (value != nil) {
            return;
        }
    }

    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    NSString *title = nil;
    NSString *detail = nil;
    NSString *cancelButton = @"好的";
    NSString *otherButton = nil;
    if (status == kCLAuthorizationStatusRestricted) {
        title = @"无法使用定位服务";
    } else if(status == kCLAuthorizationStatusDenied) {
        title = @"定位服务已经关闭";
        detail = @"请在设置中确保定位服务开启，并且允许该程序访问地理位置";
        otherButton = @"去设置";
        cancelButton = @"暂不";
    }

    // 如果用户关闭了定位服务，或者某种未预料到的原因使title总不为空
    if ([title isNonEmpty]) {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:detail preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:cancelButton style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
            if (key != nil) {
                [GMCache storeObjectAtDocumentPathWithkey:key object:key];
            }
        }]];

        if ([otherButton isNonEmpty]) {
            [alert addAction:[UIAlertAction actionWithTitle:otherButton style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
                [GMLocationManager jumpToSettings];
                if (key != nil) {
                    [GMCache storeObjectAtDocumentPathWithkey:key object:key];
                }
            }]];
        }

        // 虽然选择城市页是被present的，但是 visibleViewController 仍然能访问到该controller
        [AppDelegate.visibleController presentViewController:alert animated:YES completion:NULL];
    }
}


+ (void)jumpToSettings {
    // iOS8及以后可以跳转到该app的专有设置页
    NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10.0")) {
        [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:NULL];
    } else {
        [[UIApplication sharedApplication] openURL:url];
    }
}

/**
 *  @author wangyang, 16-02-24 15:02:30
 *
 *  @brief 只在内部调用，以请求相应权限。如果定位服务异常不做任何处理，因为有相应的需求来处理这定位异常
 *  @since 5.9.1
 */
- (void)requestAuthorization{
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];

    if(status == kCLAuthorizationStatusNotDetermined){
        // 弹出是否允许使用位置服务
        [self.locationManager requestWhenInUseAuthorization];
        [self _updatingLocation];
    }else if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse){
        [self _updatingLocation];
    }
}

- (void)updateLocation {
    CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
    BOOL enable = [CLLocationManager locationServicesEnabled];
    if (enable && (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse)) {
        [self _updatingLocation];
    }
}

- (void)_updatingLocation {
    [self.locationManager startUpdatingLocation];
    _firstTime = YES;
}

#pragma mark - CLLocationManagerDelegate
// 地理位置发生改变时触发
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    // 注意此处可能会被多次调用，这是CoreLocation中的正常现象
    self.location = locations.lastObject;
    [self finishUpdatingLocation];

}

// 定位失误时触发
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    self.location = [[CLLocation alloc] initWithLatitude:0 longitude:0];
    [self finishUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    // 如果不允许访问地理位置，直接完成
    if (status == kCLAuthorizationStatusRestricted || status == kCLAuthorizationStatusDenied) {
        [self finishUpdatingLocation];
    }else if (status == kCLAuthorizationStatusAuthorizedAlways || status == kCLAuthorizationStatusAuthorizedWhenInUse){
        [self _updatingLocation];
    }
}

- (void)finishUpdatingLocation {
    for (int i = 0; i < _blocks.count; i ++) {
        LocationFinishBlock block = _blocks[i];
        block(self.location);
    }
}

// iOS返回的是国际坐标，需要转换为百度坐标
- (void)setLocation:(CLLocation *)location {
    if (location == nil) {
        return;
    }
    CLLocationCoordinate2D coordinate = [JZLocationConverter wgs84ToBd09:location.coordinate];
    // ZX 不开启定位时，不需要传经纬度 经过上边方法转换后有小误差，手动置为0
    if (![GMLocationManager hasOpenLocationService]) {
        coordinate.latitude = 0;
        coordinate.longitude = 0;
    }
    _location = [[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude];
}

#pragma mark - Block Manage 
- (void)addBlock:(LocationFinishBlock)block {
    [_blocks addObject:block];
}

- (void)removeLast {
    [_blocks removeLastObject];
}

@end
