import * as React from 'react'
import PropTypes from 'prop-types'

import Throttle from '../core/Throttle'
import NervNode from '../core/NervNode'
import Scheduler from '../core/Scheduler'
import Listener, { RawListenerType } from '../core/Listener'
import Request from '../core/Request'
import NervComponent from './NervComponent'
import NervAdHoc from './NervAdHoc'

export interface CenterContextType {
  __NERV__CenterNode: NervNode
  __NERV__MountToParent: Function
  __NERV__UnmountFromParent: Function
}

// TODO 监控 request 在 nervTree 之间的流转情况
export const requestScheduler = new Scheduler('package')

export default class NervCenter<T, S> extends React.PureComponent<T, S> {
  private name: string
  
  protected readonly nervNode: NervNode

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

  constructor(props: T) {
    super(props)

    this.name = this.constructor.name

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

    this.nervNode = new NervNode(name, 'CenterNode', lsr, it, ot, null)
  }

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

  protected listenNerv() :RawListenerType {
    return {}
  }

  protected bindAdHoc(child: NervAdHoc) {
    child.nervNode.setCenterNervNode(this.nervNode)
    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 this.dispatchDown(path, data, async)
  }

  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)
    })
  }
}