#import "RNLocationBackgroundTimer.h"

#import "RCTDefines.h"
#import <React/RCTEventDispatcher.h>
#import "RCTLog.h"

#import <CoreLocation/CoreLocation.h>

NSString *const kLocationTimeout = @"onLocationTimeout";
NSString *const kLocationResponse = @"onLocationResponse";

NSString *const kDriverModeKey = @"driverMode";
NSString *const kDeliveryType = @"deliveryType";
NSString *const kStatusKey = @"status";
NSString *const kURLKey = @"url";

@interface RNLocationBackgroundTimer()<CLLocationManagerDelegate>
@property (strong, nonatomic, readwrite) NSTimer *nativeTimer;
@property (strong, nonatomic, readwrite) NSURLSession *session;
@property (strong, nonatomic, readwrite) NSString *authToken;
@property (strong, nonatomic, readwrite) NSString *baseURL;
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CLLocation *currentLocation;
@property (nonatomic) BOOL isTimingOut;

@end

@implementation RNLocationBackgroundTimer

@synthesize bridge = _bridge;

- (instancetype)init
{
    if (self = [super init])
    {
        self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.pausesLocationUpdatesAutomatically = NO;
        self.locationManager.allowsBackgroundLocationUpdates = YES;
        self.locationManager.delegate=self;
        self.locationManager.desiredAccuracy=kCLLocationAccuracyBest;
        
        if([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
        {
            [self.locationManager requestAlwaysAuthorization];
        }
        else
        {
            [self.locationManager startUpdatingLocation];
        }
        
        [UIApplication sharedApplication].idleTimerDisabled=YES;
    }
    return self;
}

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(setBaseAPIURL:(NSString *)url)
{
    RCTLogInfo(@"url %@", url);
    self.baseURL = url;
    RCTLogInfo(@"Location setBaseURL %@", self.baseURL);
}

RCT_EXPORT_METHOD(startPostingLocation:(NSString *)authToken interval:(NSInteger)interval)
{
    if (!authToken || authToken.length == 0 || interval <= 0) {
        RCTLogInfo(@"invalid input parameter passed");
        return;
    }
    
    if (!self.baseURL) {
        RCTLogInfo(@"base url needs to be set with setBaseURL");
        return;
    }
    
    self.authToken = authToken;
    
    double seconds = interval / 1000.0;
    RCTLogInfo(@"Location interval in seconds : %@", @(seconds));
    
    if (self.nativeTimer) {
        [self stopPostingLocation];
    }
    self.nativeTimer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(postLocation:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.nativeTimer forMode:NSRunLoopCommonModes];
}

- (void)postLocation:(NSTimer *)timer
{
    RCTLogInfo(@"postLocation !! timer %@ self: %@", timer, self);
    if (self.authToken && self.baseURL) {
        NSURL *trackURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/track", self.baseURL]];
        RCTLogInfo(@"Attempt to post from app delegate to the server with trackURL %@", trackURL);
        
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:trackURL];
        [request addValue:self.authToken forHTTPHeaderField:@"X-Auth-Token"];
        [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        [request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
        request.HTTPMethod = @"POST";
        
        if (self.currentLocation) {
            NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
            [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
            [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
            
            NSDate *now = [NSDate date];
            NSString *iso8601String = [dateFormatter stringFromDate:now];
            
            NSDictionary *params =  @{@"latitude":@(self.currentLocation.coordinate.latitude), @"longitude":@(self.currentLocation.coordinate.longitude), @"recordedtime":(iso8601String)};
            RCTLogInfo(@"Attempt to post track information with params %@", params);
            
            NSError *error = nil;
            NSData *data = [NSJSONSerialization dataWithJSONObject:params
                                                           options:kNilOptions error:&error];
            if (data && !error) {
                [request setHTTPBody:data];
                NSURLSessionDataTask *postDataTask = [self.session dataTaskWithRequest:request
                                                                     completionHandler:^(NSData *data, NSURLResponse *response, NSError *requestError) {
                                                                         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                                         RCTLogInfo(@"Status code is %@", @([httpResponse statusCode]));
                                                                         if (requestError || [httpResponse statusCode] != 200) {
                                                                             RCTLogInfo(@"Error returned %@", requestError);
                                                                             RCTLogInfo(@"response is %@", requestError);
                                                                         }
                                                                         
                                                                         NSDictionary *locationResopnse;
                                                                         NSInteger driverMode = -1;
                                                                         NSString *deliveryType = @"";
                                                                         if (data) {
                                                                             locationResopnse = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                                                                             if (locationResopnse && locationResopnse[kDriverModeKey]) {
                                                                                 driverMode = [locationResopnse[kDriverModeKey] integerValue];
                                                                             }
                                                                            if (locationResopnse && locationResopnse[kDeliveryType]) {
                                                                                deliveryType = locationResopnse[kDeliveryType];
                                                                            }
                                                                         }
                                                                         [self broadcastResponseInfo:request.URL.absoluteString
                                                                                          statusCode:httpResponse.statusCode
                                                                                          driverMode:driverMode
                                                                                          deliveryType:deliveryType];
                                                                         [self broadcastTimeoutEvent:requestError];
                                                                     }];
                
                [postDataTask resume];
            }
        }
    }
}

- (void)broadcastResponseInfo:(NSString *)requestUrl statusCode:(NSInteger)code driverMode:(NSInteger)driverMode deliveryType:(NSString *)deliveryType
{
    
    if (!requestUrl || !code) {
        requestUrl = @"bad value returned from server";
        code = -1;
    }
    
    [self.bridge.eventDispatcher sendAppEventWithName:kLocationResponse
                                                 body:@{kStatusKey: @(code), kURLKey: requestUrl, kDriverModeKey : @(driverMode), kDeliveryType : deliveryType}];
}

- (void)broadcastTimeoutEvent:(NSError *)error
{
    self.isTimingOut = (error && error.code == kCFURLErrorNotConnectedToInternet);
    [self.bridge.eventDispatcher sendAppEventWithName:kLocationTimeout
                                                 body:@{@"timeout": @(self.isTimingOut) }];
}

RCT_EXPORT_METHOD(stopPostingLocation)
{
    RCTLogInfo(@"stopPostingLocation");
    [self.nativeTimer invalidate];
    self.nativeTimer = nil;
}

#pragma - CLLocationManagerDelegate

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    if(status==kCLAuthorizationStatusAuthorizedAlways)
    {
        [self.locationManager startUpdatingLocation];
    }
}

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    self.currentLocation=locations.lastObject;
}

@end

