import { RenderContainer } from "./renderContainer"
import { EventsMap } from "./types"
import { isFunction, nextTick } from "./utils"

type GetConstructType<T extends abstract new(...args: any)=>any> = 
  T extends abstract new (...args: infer P) => any
    ? P
    : never

type ChildType = RenderContainer | FYGE.Container

type ChildrenTypeArray = Array<RenderContainer | FYGE.Container>

export function addChildFromParent(parent: FYGE.Container, children: ChildType[] | ChildType) {
  if (Array.isArray(children)) {
    children.forEach((child) => {
      if (Array.isArray(child)) {
        addChildFromParent(parent, child)
      } else {
        if (typeof child === 'number') {
          // @ts-ignore
          child = child + ''
        }
        if (typeof child === 'string') {
          const t = new FYGE.TextField()
          t.text = child
          t.fillColor = '#000000'
          // @ts-ignore
          child = t
        }
        if (child instanceof RenderContainer) {
          const node = child.render()
          node! && parent.addChild(node)
        } else {
          // 可能createElement 会return null or undefined
          child && parent.addChild(child)
        }
      }
    })
  } else {
    // children 不可能是 RenderContainer
    if (children instanceof RenderContainer) {
      const node = children.render()
      node! && parent.addChild(node)
    } else {
      parent.addChild(children)
    }
  }
}

export function createElement<K extends new(p?: any) => ChildType, T extends ChildType>(ConstructType: K, props: Record<string, any> & {
  inlineProps: Record<string, any>,
  children: any[],
  ref?: (c: ChildType) => void,
} & EventsMap, ...children: ChildrenTypeArray): FYGE.Container {
  if (typeof ConstructType === 'string') {
    throw TypeError('Dream create cannot effect for string element type')
  }
  

  // TODO Object.freeze
  props = props || Object.create(null)
  // delete props.children
  props.children = children
  // Object.freeze(props)
  // type tk = GetConstructType<K>
  let eleins = new ConstructType(props)
  if (
    !(eleins instanceof RenderContainer || eleins instanceof FYGE.Container)
    ) {
    throw TypeError(`class ${ConstructType} must extends RenderContainer or FYGE.Container...`)
  }

  if (props.className) {
    // @ts-ignore
    eleins['className'] = props.className
  }

  const ref = props.ref
  if (typeof ref === 'function') {
    nextTick(function() {
      ref(eleins)
    })
    
    // 别往下传播了
    delete props.ref
  }

  if (eleins instanceof FYGE.Container) {
    const inlineProps = props.inlineProps
    if (inlineProps) {
      // 简单点直接赋值
      for (let k in inlineProps) {
        // @ts-ignore 里面是 getter 和 setter 写的
        inlineProps[k] !== undefined && (eleins[k] = inlineProps[k])
      }
    }

    if (isFunction(props.onClick)) {
      eleins.addEventListener(FYGE.MouseEvent.CLICK, props.onClick!)
    }
    if (isFunction(props.onClickCapture)) {
      eleins.addEventListener(FYGE.MouseEvent.CLICK, props.onClickCapture!, eleins, true)
    }
    if (isFunction(props.onEnterFrame)) {
      eleins.addEventListener(FYGE.Event.ENTER_FRAME, props.onEnterFrame!)
    }
    if (isFunction(props.onAddedToStage)) {
      eleins.addEventListener(FYGE.Event.ADDED_TO_STAGE, props.onAddedToStage!)
    }
    if (isFunction(props.onRemoveFromStage)) {
      eleins.addEventListener(FYGE.Event.REMOVED_FROM_STAGE, props.onRemoveFromStage)
    }

    // 被移除的时候直接 去 清除 Tween
    eleins.addEventListener(FYGE.Event.REMOVED_FROM_STAGE, function(e: any) {
      e && e.target && FYGE.Tween.removeTweens(e.target)
    })

    // _noap 不会加到后面
    !props._noap && addChildFromParent(eleins, children)
  } else if (eleins instanceof RenderContainer) {
    // 因为是 倒序的 nextTick 这里就先 didRendered 注册一下， 先让 render 里面的 ref 跑完
    nextTick(function() {
      (eleins as RenderContainer).didRendered();
      (eleins as RenderContainer).isDidRendered = true
    }, eleins)
    const node: FYGE.Container = eleins.render()
    node.addEventListener(FYGE.Event.REMOVED_FROM_STAGE, () => {
      (eleins as RenderContainer).unMount()
    })
    return node
  }
  
  return eleins
}