// Copyright 2015-present 650 Industries. All rights reserved.

#import "EXDevMenuManager.h"
#import "EXDevMenuWindow.h"
#import "EXHomeModule.h"
#import "EXDevMenuMotionInterceptor.h"
#import "EXDevMenuGestureInterceptor.h"

static NSString *kEXDevMenuMotionGestureEnabled = @"EXDevMenuMotionGestureEnabled";
static NSString *kEXDevMenuTouchGestureEnabled = @"EXDevMenuTouchGestureEnabled";

@interface EXDevMenuManager ()

@property (nonatomic, strong) EXDevMenuWindow *window;

@end

@implementation EXDevMenuManager

+ (instancetype)sharedInstance
{
  static EXDevMenuManager *manager;
  static dispatch_once_t once;
  dispatch_once(&once, ^{
    if (!manager) {
      manager = [EXDevMenuManager new];

      // Read initial attributes from user defaults.
      id motionGestureEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:kEXDevMenuMotionGestureEnabled];
      id touchGestureEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:kEXDevMenuTouchGestureEnabled];
      manager.interceptMotionGesture = motionGestureEnabled != nil ? [motionGestureEnabled boolValue] : YES;
      manager.interceptTouchGesture = touchGestureEnabled != nil ? [touchGestureEnabled boolValue] : YES;
    }
  });
  return manager;
}

#pragma mark - API

- (BOOL)interceptMotionGesture
{
  return [EXDevMenuMotionInterceptor isInstalled];
}

- (void)setInterceptMotionGesture:(BOOL)interceptMotionGesture
{
  interceptMotionGesture ? [EXDevMenuMotionInterceptor install] : [EXDevMenuMotionInterceptor uninstall];
  [[NSUserDefaults standardUserDefaults] setBool:interceptMotionGesture forKey:kEXDevMenuMotionGestureEnabled];
}

- (BOOL)interceptTouchGesture
{
  return [EXDevMenuGestureInterceptor isInstalled];
}

- (void)setInterceptTouchGesture:(BOOL)interceptTouchGesture
{
  interceptTouchGesture ? [EXDevMenuGestureInterceptor install] : [EXDevMenuGestureInterceptor uninstall];
  [[NSUserDefaults standardUserDefaults] setBool:interceptTouchGesture forKey:kEXDevMenuTouchGestureEnabled];
}

- (RCTBridge *)mainBridge
{
  return [_delegate mainBridgeForDevMenuManager:self];
}

- (BOOL)isVisible
{
  return _window ? !_window.hidden : NO;
}

- (BOOL)open
{
  if (![self canChangeVisibility:YES]) {
    return NO;
  }
  [self setVisibility:YES];
  return YES;
}

- (BOOL)close
{
  if (![self canChangeVisibility:NO]) {
    return NO;
  }
  EXHomeModule *homeModule = [[self mainBridge] moduleForName:@"ExponentKernel"];

  if (homeModule) {
    // This will trigger `closeWithoutAnimation` once the animation is finished.
    [homeModule requestToCloseDevMenu];
  } else {
    // Module not found, close immediately?
    [self closeWithoutAnimation];
  }

  return YES;
}

- (BOOL)toggle
{
  return self.isVisible ? [self close] : [self open];
}

- (void)closeWithoutAnimation
{
  [self setVisibility:NO];
}

#pragma mark - delegate stubs

- (BOOL)canChangeVisibility:(BOOL)visible
{
  if (self.isVisible == visible) {
    return NO;
  }
  if ([_delegate respondsToSelector:@selector(devMenuManager:canChangeVisibility:)]) {
    return [_delegate devMenuManager:self canChangeVisibility:visible];
  }
  return YES;
}

#pragma mark - internal

- (void)setVisibility:(BOOL)visible
{
  dispatch_async(dispatch_get_main_queue(), ^{
    if (!self->_window) {
      self->_window = [EXDevMenuWindow new];
    }
    if (visible) {
      [self->_window makeKeyAndVisible];
    } else {
      self->_window.hidden = YES;
    }
  });
}

@end
