import * as React from 'react'
import PropTypes from 'prop-types'
import Throttle, { RawThrottleType } from '../core/Throttle'
import Listener, { RawListenerType } from '../core/Listener'
import Request from '../core/Request'
import NervNode from '../core/NervNode'
import { CenterContextType } from './NervCenter'
import Validator from '../core/Validator'
import NervAdHoc from './NervAdHoc'


// 连接 NervNode 与 Component
export default class NervComponent<T, S> extends React.PureComponent<T, S> {
  public readonly nervNode: NervNode

  protected inThrottle :RawThrottleType = null
  protected outThrottle :RawThrottleType = null

  private ownerContext: CenterContextType
  private name: string

  static contextTypes = {
    __NERV__CenterNode: PropTypes.object,
    __NERV__MountToParent: PropTypes.func,
    __NERV__UnmountFromParent: PropTypes.func,
  }

  static childContextTypes = {
    __NERV__CenterNode: PropTypes.object,
    __NERV__MountToParent: PropTypes.func,
    __NERV__UnmountFromParent: PropTypes.func,
  }

  constructor(props: T, context: CenterContextType) {
    super(props, context)

    this.name = this.constructor.name

    const it = new Throttle(this.name, this.inThrottle)
    const ot = new Throttle(this.name, this.outThrottle)
    const lsr = new Listener(this.name, this.listenNerv())

    this.ownerContext = context
    this.nervNode = new NervNode(this.name, 'ComponentNode', lsr, it, ot, context.__NERV__CenterNode)
    this.context.__NERV__MountToParent(this.nervNode)
  }

  public getChildContext() {
    return {
      __NERV__CenterNode: this.ownerContext.__NERV__CenterNode,
      __NERV__MountToParent: this.nervNode.bind.bind(this.nervNode),
      __NERV__UnmountFromParent: this.nervNode.unbind.bind(this.nervNode),
    }
  }

  public componentWillUnmount() {
    this.ownerContext.__NERV__UnmountFromParent(this.nervNode)
  }

  protected listenNerv() :RawListenerType {
    return {}
  }

  protected bindAdHoc(child: NervAdHoc) {
    child.nervNode.setCenterNervNode(this.context.__NERV__CenterNode)
    this.nervNode.bind(child.nervNode)
  }

  protected unbindAdHoc(child: NervAdHoc) {
    this.nervNode.unbind(child.nervNode)
  } 

  protected dispatch(path: string, data: any = null, async: boolean = true) :Promise<{data: any, error: any}> {
    return new Promise(resolve => {
      const request = new Request(path, data, resolve, async)
      this.nervNode.dispatch(request)
    })
  }

  protected dispatchUp(path: string, data: any = null, async: boolean = true) :Promise<{data: any, error: any}> {
    return new Promise(resolve => {
      const request = new Request(path, data, resolve, async)
      this.nervNode.dispatchUp(request)
    })
  }

  protected dispatchDown(path: string, data: any = null, async: boolean = true) :Promise<{data: any, error: any}> {
    return new Promise(resolve => {
      const request = new Request(path, data, resolve, async)
      this.nervNode.dispatchDown(request)
    })
  }

  protected broadcast(path: string, data: any = null, async: boolean = true) :Promise<{data: any, error: any}> {
    return new Promise(resolve => {
      const request = new Request(path, data, resolve, async)
      this.nervNode.broadcast(request)
    })
  }
}