import Dream from "../../Dream"
import { Container, DreamDotAni } from "../../Dream/UI"
import { createImperativePromise, ImperativePromise } from "../awesome-promise"
import { ModalCtroller } from "../ModalControl"
import { RES } from "../RES"

type SingleLoader = (res: string, cb: (d: any, ...args: any[]) => any) => Promise<any>

type UsePreloadType = {
  resList: string[]
  resPreloadFunc: Record<string, SingleLoader>,
  loadingComponent: any,
  preAction?: () => Promise<any>
}

const defaultConfig: UsePreloadType = {
  resList: [],
  resPreloadFunc: {
    others: RES.getResAsync
  },
  loadingComponent: function() {return new FYGE.Container()},
  preAction: () => Promise.resolve()
}

const loadResourceOnce = (function() {
  interface QueueItem {
    queue: {
      onLoad: Function,
      onProgress?: Function,
    }[],
    data: any // 这里可以 通过 data 字段来判断是否 加载过， 如果是请求的话， 可能就不需要了， 直接 delete loading[url] 就好了
    wrapPrevPromise?: ImperativePromise<void>
  }
  const loading: Record<string, QueueItem> = {}
  return function(url: string, onLoad: Function, onProgress: Function, loadFunc: (s: string, rc:(d: any) => void)=>Promise<any>, force?: boolean) {
    if (!force) {
      if (loading[url] && loading[url].data) {
        onLoad(true, loading[url].data)
        // TODO onProgress 
        return 
      }
      // 有的话， 说明正在加载中
      if (loading[url]) {
        loading[url].queue.push({
          onLoad,
          onProgress
        })
        return
      }
    } else {
      // cancel之前的 load Promise
      loading[url] && loading[url].wrapPrevPromise && loading[url].wrapPrevPromise.cancel()
    }
    // 否则的话， 就是没有加载
    const currItem: QueueItem = loading[url] || (loading[url] = {
      queue: [],
      data: null,
    })
    currItem.queue.push({
      onLoad,
      onProgress
    })
      
    const type = url.split('.').pop()
    loading[url].wrapPrevPromise = createImperativePromise(
      new Promise<{
        url: string,
        suc: boolean,
        data?: any
      }>((resolve) => {
        loadFunc(url, (d) => {
          if (d) {
            resolve({
              url,
              suc: true,
              data: d
            })
          } else {
            resolve({
              suc: false,
              url
            })
          }
        })
      }).then((cfg) => {
        // 防止 force 的时候，又 重新跑多余的逻辑
        while (currItem.queue.length) {
          currItem.queue.shift().onLoad(cfg.suc, cfg.data)
        }
        if (!cfg.suc) {
          delete loading[cfg.url]
        } else {
          currItem.data = cfg.data
          return cfg.data
        }
      })
    )

    return loading[url].wrapPrevPromise.promise
  }
})()


/**
 * 预先加载资源
 * @param resouces 资源列表
 * @param parallelMode 是否并行加载
 * @param func 加载资源方法
 * @returns
 */
 export function onPreloadResource(
  resouces: string[],
  parallelMode: boolean = false,
  func: { [x: string]: SingleLoader },
  onProcess?: (loaded: number, total: number) => void,
  onLoaded?: (total: number) => void,
) {
  if (!resouces || resouces.length === 0) {
    
    return onLoaded(0);
  }
  return new Promise(async (resolve) => {
    let totalCount = resouces.length,
      loadedCount = 0;
    // 不管加载有没有成功 都 loaded +1
    const loadedSucOrError = () => {
      loadedCount++;
      try {
        onProcess && onProcess(loadedCount, totalCount);
        if (loadedCount === totalCount) {
          onLoaded && onLoaded(totalCount);
        }
      } catch(e) {
        console.error('onProcess or on Loaded exec Error:' + e);
      }
      return
    }

    const loadFunc = async function(item: string) {
      const type = item.split('.').pop()
      const re: any = await loadResourceOnce(item, () => { }, () => { }, func[type] || func['others'])
      loadedSucOrError()
      if (!re) {
        console.error('onPreloadOnce Error: ' + item)
      }
      return re
    }

    if (parallelMode) {
      await Promise.all(
        resouces.map((item: string) => {
          return loadFunc(item)
        }),
      );
    } else {
      for (let i = 0; i < resouces.length; i++) {
        const item = resouces[i]
        await loadFunc(item)
      }
    }
    resolve(loadedCount);
  });
}

export function UsePreload(cfg: Partial<UsePreloadType>) {
  const finalCfg = Object.assign({}, defaultConfig, cfg, {
    resPreloadFunc: Object.assign({}, defaultConfig.resPreloadFunc, cfg.resPreloadFunc)
  })
  return function(Node: any): any {
    return class extends Dream.RenderContainer<{
      onLoaded?: () => any
    }> {
      RoleCont: FYGE.Container
      dreamType: any = 'preloadComponent'

      render() {
        const LoadingCompent = finalCfg.loadingComponent

        // TODO 这里感觉会和 nextTick 的时序发生 冲突
        // nextTick 用Promise.resolve的话，就是默认 
        // nextTick 包一层 保证时序
        Dream.nextTick(() => {
          Promise.all([
            onPreloadResource(
              finalCfg.resList,
              true,
              finalCfg.resPreloadFunc,
              (loaded, total) => {
    
              },
              () => {
                
              }
            ),
            finalCfg.preAction()
          ]).then(() => {
            // 这里用nextTick 包一层
            Dream.nextTick(() => {
              this.props.onLoaded && this.props.onLoaded()
            })
            LoadingCompent && ModalCtroller.closeModal(LoadingCompent)
            const {onLoaded, ...otherProps} = this.props
            if (this.RoleCont) {
              this.RoleCont.addChild(<Node {...otherProps}></Node>)
            }
          })
        })
        
        // 显示预加载的loading
        LoadingCompent && ModalCtroller.showModal(LoadingCompent, {}, {
          center: false
        })

        return <Container
        className="preload-cont"
        ref={el => {
          this.RoleCont = el
        }}>
        </Container>
      }
    }
  }
}