content: import EventEmitter from 'events'
  import forOf from 'object-forof'
  import Packet from 'chix-flow/lib/packet'
  import XNode from 'chix-flow/lib/BaseNode'
  import Debug from 'debug'
  import React from 'react'
  import loadComponent from './loadComponent'
  
  const debug = Debug('chix:ReactNode')
  
  /**
   * A ReactNode wraps a normal React Component
   *
   * The react component must be defined as a require, within the node definition.
   *
   * Props are used as input ports.
   *
   * Example definition:
   *
   * "require": { "my-comp": "my/repos/someComponent" }
   * "ports": {
   *    "input": {
   *      "label": {
   *        "type": "string
   *      }
   *      "someHandler": {
   *         "type": "boolean"
   *      }
   *    },
   *    "output": {
   *      "component": {} // needed for composition
   *      "someHandler": {
   *         "type": "any"
   *      }
   *    }
   *  }
   *
   * 'My Label'           ->        label MyComp
   * true                 ->  someHandler MyComp
   * MyComp component     ->      element Render
   * MyComp onDismiss [0] ->          msg Console
   *
   * @param {type} id
   * @param {type} node
   * @param {type} identifier
   */
  export default class ReactNode extends XNode {
    constructor (id, node, identifier) {
      super(id, node, identifier)
  
      this.id = id
  
      this.ns = node.ns
      this.name = node.name
  
      this.type = 'ReactNode'
  
      this.ports = {
        input: {},
        output: {}
      }
  
      // does not seem to set required on the input port?
      this.createPorts(node.ports)
  
      this.identifier = identifier
  
      debug('%s load component', this.identifier)
      this.componentClass = loadComponent(node)
  
      this.stateEmitter = new EventEmitter()
  
      this.status = 'ready'
    }
  
    createComponent (initialState) {
      const stateEmitter = this.stateEmitter
      const componentClass = this.componentClass
  
      class ReactNode extends React.Component {
        constructor (props, context) {
          super(props, context)
  
          this.state = initialState
  
          this.stateListener = this.setState.bind(this)
  
          stateEmitter.on('state', this.stateListener)
        }
  
        componentWillUnmount () {
          stateEmitter.off('state', this.stateListener)
        }
  
        render () {
          return React.createElement(
            componentClass,
            this.state
          )
        }
      }
  
      this.component = React.createElement(ReactNode)
  
      const p = new Packet(
        this.component,
        'ReactElement'
      )
  
      // needed during setup otherwise target is not yet created.
      setTimeout(() => {
        debug('%s: Sending component', this.identifier)
        this.sendPortOutput('component', p)
      })
    }
  
    isEventHandler (port) {
      return port.type === 'boolean' && this.ports.output[port.name]
    }
  
    allPortsFilled () {
      let allFilled = true
  
      forOf((name, port) => {
        if (!port.isFilled()) {
          allFilled = false
        }
      }, this.ports.input)
  
      return allFilled
    }
  
    setRealType() {
    }
  
    onPortFill () {
      debug('%s:onPortFill running', this.identifier)
      const input = {}
  
      if (this.component) {
        // always update state on every portfill
        forOf((name, port) => {
          const data = port.read().read()
  
          if (!this.isEventHandler(port)) { // only set handler during initialization
            input[port.name] = data
          }
        }, this.ports.input)
  
        debug('%s:onPortFill emitting state', this.identifier)
  
        this.stateEmitter.emit('state', input)
      } else {
        if (this.allPortsFilled()) {
          forOf((name, port) => {
            const data = port.read().read()
  
            if (this.isEventHandler(port)) {
              if (data === true) {
                input[ port.name ] = (...args) => {
                  const event = {
                    name: `${this.ns}_${this.name}`.toUpperCase(),
                    arguments: args
                  }
  
                  const eventPacket = new Packet(event, 'object')
  
                  this.sendPortOutput(port.name, eventPacket)
                }
              }
            } else if (name === 'children') {
              // etc. more options.
              if (port.propType === 'element') {
                input[port.name] = React.createElement('div', null, data)
                  // data.pop()
              } else {
                input[port.name] = data
              }
            } else {
              input[port.name] = data
            }
          }, this.ports.input)
  
          debug('%s:onPortFill creating component', this.identifier)
  
          this.createComponent(input)
        } else {
          debug('%s:onPortFill ports not ready yet', this.identifier)
        }
      }
    }
  
    $setContextProperty (port, p) {
      debug('%s:%s set context', this.identifier, port)
      let data
  
      if (p instanceof Packet) {
        data = p.read(this)
      } else {
        data = p
      }
  
      this.stateEmitter.emit('state', { [port]: data })
    }
  
    $portIsFilled (/* port */) {
      // always ready to receive new props.
      return false
    }
  
    /**
     *
     * Holds all input until release is called
     *
     * @public
     */
    hold () {
      this.setStatus('hold')
    }
  
    shutdown (cb) {
      this.stateEmitter.off('state', this.stateListener)
  
      cb(null)
    }
  
    hasShutdown () {
      return true
    }
  
    /**
     *
     * Releases the node if it was on hold
     *
     * @public
     */
    release () {
      this.setStatus('ready')
  
      if (this.__halted) {
        this.__halted = false
        // not much to do
      }
    }
  }
  