/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

#import "RCTMap.h"

#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTPolyline.h"
#import "RCTUtils.h"

const CLLocationDegrees RCTMapDefaultSpan = 0.005;
const NSTimeInterval RCTMapRegionChangeObserveInterval = 0.1;
const CGFloat RCTMapZoomBoundBuffer = 0.01;

@implementation RCTMap
{
  UIView *_legalLabel;
  CLLocationManager *_locationManager;
}

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

    _hasStartedRendering = NO;

    // Find Apple link label
    for (UIView *subview in self.subviews) {
      if ([NSStringFromClass(subview.class) isEqualToString:@"MKAttributionLabel"]) {
        // This check is super hacky, but the whole premise of moving around
        // Apple's internal subviews is super hacky
        _legalLabel = subview;
        break;
      }
    }
  }
  return self;
}

- (void)dealloc
{
  [_regionChangeObserveTimer invalidate];
}

- (void)layoutSubviews
{
  [super layoutSubviews];

  if (_legalLabel) {
    dispatch_async(dispatch_get_main_queue(), ^{
      CGRect frame = _legalLabel.frame;
      if (_legalLabelInsets.left) {
        frame.origin.x = _legalLabelInsets.left;
      } else if (_legalLabelInsets.right) {
        frame.origin.x = self.frame.size.width - _legalLabelInsets.right - frame.size.width;
      }
      if (_legalLabelInsets.top) {
        frame.origin.y = _legalLabelInsets.top;
      } else if (_legalLabelInsets.bottom) {
        frame.origin.y = self.frame.size.height - _legalLabelInsets.bottom - frame.size.height;
      }
      _legalLabel.frame = frame;
    });
  }
}

#pragma mark Accessors

- (void)setShowsUserLocation:(BOOL)showsUserLocation
{
  if (self.showsUserLocation != showsUserLocation) {
    if (showsUserLocation && !_locationManager) {
      _locationManager = [CLLocationManager new];
      if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
        [_locationManager requestWhenInUseAuthorization];
      }
    }
    super.showsUserLocation = showsUserLocation;

    // If it needs to show user location, force map view centered
    // on user's current location on user location updates
    _followUserLocation = showsUserLocation;
  }
}

- (void)setRegion:(MKCoordinateRegion)region animated:(BOOL)animated
{
  // If location is invalid, abort
  if (!CLLocationCoordinate2DIsValid(region.center)) {
    return;
  }

  // If new span values are nil, use old values instead
  if (!region.span.latitudeDelta) {
    region.span.latitudeDelta = self.region.span.latitudeDelta;
  }
  if (!region.span.longitudeDelta) {
    region.span.longitudeDelta = self.region.span.longitudeDelta;
  }

  // Animate to new position
  [super setRegion:region animated:animated];
}

- (void)setAnnotations:(RCTPointAnnotationArray *)annotations
{
  NSMutableArray<NSString *> *newAnnotationIds = [NSMutableArray new];
  NSMutableArray<RCTPointAnnotation *> *annotationsToDelete = [NSMutableArray new];
  NSMutableArray<RCTPointAnnotation *> *annotationsToAdd = [NSMutableArray new];

  for (RCTPointAnnotation *annotation in annotations) {
    if (![annotation isKindOfClass:[RCTPointAnnotation class]]) {
      continue;
    }

    [newAnnotationIds addObject:annotation.identifier];

    // If the current set does not contain the new annotation, mark it as add
    if (![self.annotationIds containsObject:annotation.identifier]) {
      [annotationsToAdd addObject:annotation];
    }
  }

  for (RCTPointAnnotation *annotation in self.annotations) {
    if (![annotation isKindOfClass:[RCTPointAnnotation class]]) {
      continue;
    }

    // If the new set does not contain an existing annotation, mark it as delete
    if (![newAnnotationIds containsObject:annotation.identifier]) {
      [annotationsToDelete addObject:annotation];
    }
  }

  if (annotationsToDelete.count) {
    [self removeAnnotations:(NSArray<id<MKAnnotation>> *)annotationsToDelete];
  }

  if (annotationsToAdd.count) {
    [self addAnnotations:(NSArray<id<MKAnnotation>> *)annotationsToAdd];
  }

  NSMutableArray<NSString *> *newIds = [NSMutableArray new];
  for (RCTPointAnnotation *annotation in self.annotations) {
    if ([annotation isKindOfClass:[MKUserLocation class]]) {
      continue;
    }
    [newIds addObject:annotation.identifier];
  }
  self.annotationIds = newIds;
}

- (void)setPolylines:(RCTPolylineArray *)polylines
{
  NSMutableArray *newPolylineIds = [NSMutableArray new];
  NSMutableArray *polylinesToDelete = [NSMutableArray new];
  NSMutableArray *polylinesToAdd = [NSMutableArray new];

  for (RCTPolyline* polyline in polylines) {
    [newPolylineIds addObject:polyline.identifier];

    // If the current set does not contain the new annotation, mark it as add
    if (![self.annotationIds containsObject:polyline.identifier]) {
      [polylinesToAdd addObject:polyline];
    }
  }

  for (RCTPolyline *polyline in self.overlays) {
    if (![polyline isKindOfClass:[RCTPolyline class]]) {
      continue;
    }

    // If the new set does not contain an existing annotation, mark it as delete
    if (![newPolylineIds containsObject:polyline.identifier]) {
      [polylinesToDelete addObject:polyline];
    }
  }

  if (polylinesToDelete.count) {
    [self removeOverlays: polylinesToDelete];
  }

  if (polylinesToAdd.count) {
    [self addOverlays: polylinesToAdd];
  }

  self.polylineIds = newPolylineIds;
}

@end
