import {Subject, Observable} from 'rxjs'
import {map, merge, mergeMap, filter} from 'rxjs/operators'

import resize$ from './resize'
import scroll$ from './scroll'
import orientationChange$ from './orientationChange'

const ROOT_MARGIN_PX = 150

/*
  Adapted from the polyfill at https://github.com/WICG/IntersectionObserver
*/
function isIntersectionObserverSupported() {
  if (
    'IntersectionObserver' in window &&
    'IntersectionObserverEntry' in window &&
    'intersectionRatio' in IntersectionObserverEntry.prototype
  ) {
    // Minimal polyfill for Edge 15's lack of `isIntersecting`
    // See: https://github.com/WICG/IntersectionObserver/issues/211
    if (!('isIntersecting' in IntersectionObserverEntry.prototype)) {
      Object.defineProperty(IntersectionObserverEntry.prototype, 'isIntersecting', {
        get() {
          return this.intersectionRatio > 0
        }
      })
    }
    return true
  }
  return false
}

export default (isIntersectionObserverSupported()
  ? createIntersectionObserverBased()
  : createLegacyBased())

function createIntersectionObserverBased() {
  const intersectionObserverEntries$$ = new Subject()
  const intersectionObserver = new IntersectionObserver(callback, {
    threshold: 0,
    rootMargin: `${ROOT_MARGIN_PX}px`
  })

  function callback(entries) {
    entries.forEach(entry => {
      intersectionObserverEntries$$.next(entry)
    })
  }

  return function intersectionObservableFor(element) {
    return new Observable(observer => {
      intersectionObserver.observe(element)
      observer.next()
      return () => intersectionObserver.unobserve(element)
    }).pipe(
      mergeMap(() => intersectionObserverEntries$$.asObservable()),
      filter(event => event.target === element)
    )
  }
}

// This can be removed when intersection observer are supported by the browsers we support
function createLegacyBased() {
  function getViewport() {
    return {
      left: 0,
      right: window.innerWidth,
      top: 0,
      bottom: window.innerHeight
    }
  }

  function intersects(rect, viewport, margin) {
    return (
      rect.left <= viewport.right + margin &&
      rect.right >= viewport.left - margin &&
      rect.top <= viewport.bottom + margin &&
      rect.bottom >= viewport.top - margin
    )
  }

  function inViewport(element) {
    return () => intersects(element.getBoundingClientRect(), getViewport(), ROOT_MARGIN_PX)
  }

  return function intersectionObservableFor(element) {
    const isElementInViewport = inViewport(element)
    return Observable.of(isElementInViewport()).pipe(
      merge(resize$.merge(scroll$)),
      merge(orientationChange$),
      map(isElementInViewport),
      // todo: consider "faking" more of the IntersectionObserverEntry api if possible
      map(isIntersecting => ({isIntersecting}))
    )
  }
}

@@ -20,16 +20,43 @@
 servable
+, merge, of as observableOf
 %7D from '
@@ -73,23 +73,16 @@
 rt %7Bmap,
- merge,
  mergeMa
@@ -114,17 +114,16 @@
 rators'%0A
-%0A
 import r
@@ -2550,17 +2550,23 @@
  return 
-O
+merge(o
 bservabl
@@ -2566,18 +2566,17 @@
 servable
-.o
+O
 f(isElem
@@ -2595,64 +2595,28 @@
 t())
-.pipe(%0A      merge(resize$.merge(scroll$)),%0A      merge(
+, resize$, scroll$, 
 orie
@@ -2630,17 +2630,22 @@
 Change$)
-,
+.pipe(
 %0A      m

Fix intersection observable fallback