import React, { Component } from 'react'
import { string, number, oneOfType, node, arrayOf, bool } from 'prop-types'
import './ScrollShadow.less'

const verticalShadowColor = (colorUp, colorDown) => `linear-gradient(${colorUp}, ${colorDown}`
const horizontalShadowColor = (colorLeft, colorRight) => `linear-gradient(90deg, ${colorLeft}, ${colorRight}`

class ScrollShadow extends Component {
  static propTypes = {
    isVisible: bool,
    childCount: number,
    children: oneOfType([arrayOf(node), node]),
    maxContentHeight: oneOfType([string, number]),
    shadowHeight: oneOfType([string, number]),
    className: string,
    scrollContainerClassName: string,
    horizontalScroll: bool
  }

  scrollContainer = React.createRef()

  state = {
    showShadow: false,
    showTopShadow: false,
    showBottomShadow: true
  }

  componentDidMount () {
    this.checkShowShadow()
  }

  componentDidUpdate ({ children: prevChildren, childCount: prevChildCount, isVisible: prevIsVisible }) {
    const { children, childCount, isVisible } = this.props
    if (isVisible !== prevIsVisible
      || children && children.length !== prevChildren.length
      || childCount !== prevChildCount) {
      this.checkShowShadow()
    }
  }

  checkShowShadow () {
    const { horizontalScroll } = this.props
    const { scrollHeight, clientHeight, scrollWidth, clientWidth } = this.scrollContainer.current
    const verticalScrollShadow = scrollHeight > clientHeight
    const horizontalScrollShadow = scrollWidth > clientWidth

    this.setState({
      showShadow: horizontalScroll ? horizontalScrollShadow : verticalScrollShadow
    })
  }

  onScroll = ({ target: { scrollTop, scrollHeight, clientHeight, scrollLeft, scrollWidth, clientWidth } }) => {
    const { horizontalScroll } = this.props
    const showTopShadow = horizontalScroll ? scrollLeft !== 0 : scrollTop !== 0
    const showBottomShadow =
      horizontalScroll ? scrollWidth - scrollLeft - 1 >= clientWidth : scrollHeight - scrollTop - 1 >= clientHeight

    this.setState({
      showTopShadow,
      showBottomShadow
    })
  }

  renderBottomShadow = () => {
    const { showShadow, showBottomShadow } = this.state
    const { color, shadowHeight, shadowWidth, horizontalScroll, rightShadowContent } = this.props

    if (!showBottomShadow || !showShadow) return null

    const sizeStyle = horizontalScroll ? { width: shadowWidth } : { height: shadowHeight }
    const shadowStyle = horizontalScroll ? horizontalShadowColor('rgba(255, 255, 255, 0)', color) : verticalShadowColor('rgba(255, 255, 255, 0)', color)

    return (
      <div
        className={`scroll-${horizontalScroll ? 'right' : 'bottom'}-shadow`}
        style={{ ...sizeStyle, background: shadowStyle }}
      >
        {rightShadowContent}
      </div>
    )
  }

  renderTopShadow = () => {
    const { showTopShadow, showShadow } = this.state
    const { color, shadowHeight, shadowWidth, horizontalScroll, leftShadowContent } = this.props

    if (!showTopShadow || !showShadow) return null

    const sizeStyle = horizontalScroll ? { width: shadowWidth } : { height: shadowHeight }
    const shadowStyle = horizontalScroll ? horizontalShadowColor(color, 'rgba(255, 255, 255, 0)') : verticalShadowColor(color, 'rgba(255, 255, 255, 0)')

    return (
      <div
        className={`scroll-${horizontalScroll ? 'left' : 'top'}-shadow`}
        style={{ ...sizeStyle, background: shadowStyle }}
      >
        {leftShadowContent}
      </div>
    )
  }

  render () {
    const {
      children, maxContentHeight, maxContentWidth, className, scrollContainerStyle,
      scrollContainerClassName, disableTopShadow, horizontalScroll, containerStyle
    } = this.props

    const containerWidthStyle = maxContentWidth ? { maxWidth: maxContentWidth } : {}
    const containerHeightStyle = maxContentHeight ? { maxContentHeight: maxContentHeight } : {}
    const overflowStyle = horizontalScroll ? { overflowX: 'auto' } : { overflowY: 'auto' }

    return (
      <div className={`shadow-container ${className} ${disableTopShadow ? 'disable-top-shadow' : ''}`} style={containerStyle}>
        {this.renderTopShadow()}
        <div
          className={`scroll-container ${scrollContainerClassName}`}
          onScroll={this.onScroll}
          style={{ ...overflowStyle, ...containerWidthStyle, ...containerHeightStyle, ...scrollContainerStyle }}
          ref={this.scrollContainer}
        >
          {children}
        </div>
        {this.renderBottomShadow()}
      </div>
    )
  }
}

ScrollShadow.defaultProps = {
  horizontalScroll: false,
  color: 'rgba(0, 0, 0, 0.05)',
  scrollContainerClassName: '',
  shadowHeight: 10,
  className: '',
  scrollContainerStyle: {},
  containerStyle: {},
  leftShadowContent: null,
  rightShadowContent: null
}

export default ScrollShadow
