#import "browser/mac/bry_inspectable_web_contents_view.h"

#import "browser/inspectable_web_contents_impl.h"
#import "browser/inspectable_web_contents_view_mac.h"
#import "browser/mac/bry_inspectable_web_contents_view_private.h"

#import "content/public/browser/render_widget_host_view.h"
#import "content/public/browser/web_contents_view.h"

using namespace brightray;

@interface BRYInspectableWebContentsViewPrivate : NSObject {
@public
  InspectableWebContentsViewMac *inspectableWebContentsView;
  NSSplitView *splitView;
  NSWindow *window;
  BOOL visible;
}
@end

namespace {

NSRect devtoolsWindowFrame(NSView *referenceView) {
  auto screenFrame = [referenceView.window convertRectToScreen:[referenceView convertRect:referenceView.bounds toView:nil]];
  return NSInsetRect(screenFrame, NSWidth(screenFrame) / 6, NSHeight(screenFrame) / 6);
}

void SetActive(content::WebContents* web_contents, bool active) {
  auto render_widget_host_view = web_contents->GetRenderWidgetHostView();
  if (!render_widget_host_view)
    return;

  render_widget_host_view->SetActive(active);
}

}

@implementation BRYInspectableWebContentsView

- (instancetype)initWithInspectableWebContentsViewMac:(InspectableWebContentsViewMac *)inspectableWebContentsView {
  self = [super init];
  if (!self)
    return nil;

  _private = [[BRYInspectableWebContentsViewPrivate alloc] init];
  _private->inspectableWebContentsView = inspectableWebContentsView;
  _private->splitView = [[NSSplitView alloc] init];

  [self addSubview:_private->splitView];
  _private->splitView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
  _private->splitView.dividerStyle = NSSplitViewDividerStyleThin;
  [_private->splitView addSubview:inspectableWebContentsView->inspectable_web_contents()->GetWebContents()->GetView()->GetNativeView()];

  return self;
}

- (void)dealloc {
  [_private->window release];
  [_private->splitView release];
  [_private release];
  _private = nil;

  [super dealloc];
}

- (IBAction)showDevTools:(id)sender {
  _private->inspectableWebContentsView->inspectable_web_contents()->ShowDevTools();
}

- (void)setDevToolsVisible:(BOOL)visible {
  if (_private->visible == visible)
    return;
  _private->visible = visible;

  if ([self isDocked]) {
    if (visible) {
      [_private->window makeKeyAndOrderFront:nil];
    } else {
      [_private->window orderOut:nil];
    }
    return;
  }

  auto devToolsWebContents = _private->inspectableWebContentsView->inspectable_web_contents()->devtools_web_contents();
  auto devToolsView = devToolsWebContents->GetView()->GetNativeView();

  if (visible) {
    auto inspectedView = _private->inspectableWebContentsView->inspectable_web_contents()->GetWebContents()->GetView()->GetNativeView();
    CGRect frame = NSRectToCGRect(inspectedView.frame);
    CGRect inspectedViewFrame;
    CGRect devToolsFrame;
    CGFloat amount;
    CGRectEdge edge;
    if ([_private->splitView isVertical]) {
      amount = CGRectGetWidth(frame) * 2 / 3;
      edge = CGRectMaxXEdge;
    } else {
      amount = CGRectGetHeight(frame) * 2 / 3;
      edge = CGRectMaxYEdge;
    }
    CGRectDivide(frame, &inspectedViewFrame, &devToolsFrame, amount, edge);

    inspectedView.frame = NSRectFromCGRect(inspectedViewFrame);
    devToolsView.frame = NSRectFromCGRect(devToolsFrame);

    [_private->splitView addSubview:devToolsView];
  } else {
    [devToolsView removeFromSuperview];
  }

  [_private->splitView adjustSubviews];
}

- (BOOL)setDockSide:(const std::string&)side {
  if (side == "right") {
    _private->splitView.vertical = YES;
    [self moveToSplitView];
  } else if (side == "bottom") {
    _private->splitView.vertical = NO;
    [self moveToSplitView];
  } else if (side == "undocked") {
    [self moveToWindow];
  } else {
    return NO;
  }

  return YES;
}

- (void)moveToWindow {
  if (!_private->window) {
    auto styleMask = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask | NSTexturedBackgroundWindowMask | NSUnifiedTitleAndToolbarWindowMask;
    auto contentRect = [NSWindow contentRectForFrameRect:devtoolsWindowFrame(_private->splitView) styleMask:styleMask];
    _private->window = [[NSWindow alloc] initWithContentRect:contentRect styleMask:styleMask backing:NSBackingStoreBuffered defer:YES];
    _private->window.delegate = self;
    _private->window.releasedWhenClosed = NO;
    _private->window.title = @"Developer Tools";
    [_private->window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
    [_private->window setContentBorderThickness:24 forEdge:NSMaxYEdge];
  }

  auto devToolsWebContents = _private->inspectableWebContentsView->inspectable_web_contents()->devtools_web_contents();
  auto devToolsView = devToolsWebContents->GetView()->GetNativeView();

  NSView *contentView = _private->window.contentView;
  devToolsView.frame = contentView.bounds;
  devToolsView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;

  [contentView addSubview:devToolsView];
  [_private->window makeKeyAndOrderFront:nil];
  [_private->splitView adjustSubviews];
}

- (void)moveToSplitView {
  [_private->window orderOut:nil];

  auto devToolsWebContents = _private->inspectableWebContentsView->inspectable_web_contents()->devtools_web_contents();
  auto devToolsView = devToolsWebContents->GetView()->GetNativeView();

  [_private->splitView addSubview:devToolsView];
  [_private->splitView adjustSubviews];
}

- (BOOL)isDocked {
  auto devToolsWebContents = _private->inspectableWebContentsView->inspectable_web_contents()->devtools_web_contents();
  if (!devToolsWebContents)
    return NO;
  auto devToolsView = devToolsWebContents->GetView()->GetNativeView();
  
  return _private->window && devToolsView.window == _private->window;
}

- (void)window:(NSWindow *)window didBecomeActive:(BOOL)active {
  auto inspectable_contents = _private->inspectableWebContentsView->inspectable_web_contents();

  // Changes to the active state of the window we create only affects the dev tools contents.
  if (window == _private->window) {
    SetActive(inspectable_contents->devtools_web_contents(), active);
    return;
  }
  
  // Changes the window that hosts us always affect our main web contents. If the dev tools are also
  // hosted in this window, they are affected too.
  SetActive(inspectable_contents->GetWebContents(), active);
  if (![self isDocked])
    return;
  SetActive(inspectable_contents->devtools_web_contents(), active);
}

#pragma mark - NSView

- (void)viewWillMoveToWindow:(NSWindow *)newWindow {
  if (self.window) {
    [NSNotificationCenter.defaultCenter removeObserver:self name:NSWindowDidBecomeKeyNotification object:self.window];
    [NSNotificationCenter.defaultCenter removeObserver:self name:NSWindowDidResignKeyNotification object:self.window];
  }

  if (!newWindow)
    return;

  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowDidBecomeKey:) name:NSWindowDidBecomeKeyNotification object:newWindow];
  [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowDidResignKey:) name:NSWindowDidResignKeyNotification object:newWindow];
}

#pragma mark - NSWindowDelegate

- (BOOL)windowShouldClose:(id)sender {
  [_private->window orderOut:nil];
  return NO;
}

- (void)windowDidBecomeKey:(NSNotification *)notification {
  [self window:notification.object didBecomeActive:YES];
}

- (void)windowDidResignKey:(NSNotification *)notification {
  [self window:notification.object didBecomeActive:NO];
}

@end

@implementation BRYInspectableWebContentsViewPrivate
@end