import { createComponent, mergeProps, effect, insert, delegateEvents, template, memo, spread, className, style, setAttribute } from 'solid-js/web';
import { createContext, useContext, Switch, Match, For } from 'solid-js';
import { OriginComponent, extendsEvent, reflect, atom } from '@cn-ui/use';
import { Relative, Position, Icon, Space, DefaultIcon, Box, Button, Explorer } from '@cn-ui/core';
import mitt from 'mitt';

function readDirectory(directory) {
  return new Promise((resolve, reject) => {
    const dirReader = directory.createReader();
    let entries = [];

    const getEntries = function () {
      dirReader.readEntries(function (results) {
        if (results.length) {
          entries = entries.concat(results);
          getEntries();
        } else {
          resolve(entries);
        }
      }, function (error) {
        reject(error);
      });
    };

    getEntries();
  });
}

async function readRoll(arr) {
  for (let key in arr) {
    if (arr[key].isDirectory) {
      let result = await readDirectory(arr[key]);
      arr[key] = result;
      await readRoll(result);
    }
  }
}

function readFileEntry(fileEntry) {
  return new Promise((resolve, reject) => {
    fileEntry.file(fileData => {
      fileData.fullPath = fileEntry.fullPath;
      resolve(fileData);
    }, error => {
      reject(error);
    });
  });
}
/** 拖拽上传 */


const useDragUpload = ({
  isDragging,
  onEnd
}) => {
  return {
    async onDrop(e) {
      e.preventDefault();
      isDragging(false);

      if (DataTransferItem.prototype.webkitGetAsEntry) {
        let fileEntryList = [...e.dataTransfer.items].map(i => {
          return i.webkitGetAsEntry();
        });
        await readRoll(fileEntryList);
        fileEntryList = fileEntryList.flat(Infinity);
        const fileList = [];

        for (let item of fileEntryList) {
          let fileData = await readFileEntry(item);
          fileList.push(fileData);
        }

        onEnd(fileList);
      } else {
        onEnd([...e.dataTransfer.files].map(i => {
          i.fullPath = '/' + i.name;
          return i;
        }));
      }
    },

    onDragOver(e) {
      e.preventDefault();
      isDragging(true);
    },

    onDragLeave(e) {
      e.preventDefault();
      isDragging(false);
    }

  };
};

const UploaderContext = createContext();

const _tmpl$$4 = /*#__PURE__*/template(`<div class="h-full  rounded border border-dashed transition-colors duration-300 "></div>`, 2),
      _tmpl$2$1 = /*#__PURE__*/template(`<div class="flex h-8 w-8 items-center justify-center  text-slate-500"></div>`, 2);
const UploadingContext = createContext();

/** @zh  简单上传组件 */
const SimpleUploader = OriginComponent(props => {
  const {
    uploadControl,
    inputRef,
    isDragging
  } = useContext(UploaderContext);
  return createComponent(Relative, mergeProps(props, {
    get ["class"]() {
      return props.class('cn-uploader h-fit w-fit  p-4');
    },

    get style() {
      return props.style;
    },

    ref(r$) {
      const _ref$ = props.ref;
      typeof _ref$ === "function" ? _ref$(r$) : props.ref = r$;
    }

  }, () => useDragUpload({
    isDragging,

    async onEnd(files) {
      await uploadControl.addFiles(files);
    }

  }), {
    get children() {
      return [createComponent(Position, {
        full: true,
        "class": "p-2 ",
        top: "0",
        left: "0",
        inactive: true,

        get children() {
          const _el$ = _tmpl$$4.cloneNode(true);

          effect(_p$ => {
            const _v$ = !!isDragging(),
                  _v$2 = !isDragging();

            _v$ !== _p$._v$ && _el$.classList.toggle("bg-blue-100", _p$._v$ = _v$);
            _v$2 !== _p$._v$2 && _el$.classList.toggle("border-slate-300", _p$._v$2 = _v$2);
            return _p$;
          }, {
            _v$: undefined,
            _v$2: undefined
          });

          return _el$;
        }

      }), (() => {
        const _el$2 = _tmpl$2$1.cloneNode(true);

        _el$2.$$click = () => inputRef().click();

        insert(_el$2, createComponent(Icon, {
          name: "add",
          size: "1.5em"
        }));

        return _el$2;
      })()];
    }

  }));
});

delegateEvents(["click"]);

const _tmpl$$3 = /*#__PURE__*/template(`<div class="flex-1"></div>`, 2),
      _tmpl$2 = /*#__PURE__*/template(`<div></div>`, 2),
      _tmpl$3 = /*#__PURE__*/template(`<div class="pointer-events-none h-full bg-blue-600 opacity-10"></div>`, 2);

/** 默认的上传文件展示组件，这个可以作为上传组件的借鉴 */
const DefaultUploadList = (file, progress, uploadControl) => {
  return createComponent(Relative, {
    "class": "w-full justify-between",

    get children() {
      return [createComponent(Space, {
        "class": "cursor-default text-slate-700",

        get children() {
          return [createComponent(Icon, {
            name: "insert_drive_file",
            "class": "text-sky-600",
            size: 20
          }), memo(() => file.name), _tmpl$$3.cloneNode(true), (() => {
            const _el$2 = _tmpl$2.cloneNode(true);

            insert(_el$2, (() => {
              const _c$ = memo(() => progress() === -1, true);

              return () => _c$() ? 'canceled' : progress() + '%';
            })());

            return _el$2;
          })(), createComponent(Switch, {
            get fallback() {
              return createComponent(DefaultIcon, {
                name: "error",
                color: "red",
                onClick: () => {
                  uploadControl.upload([file]);
                }
              });
            },

            get children() {
              return [createComponent(Match, {
                get when() {
                  return progress() === 0;
                },

                get children() {
                  return createComponent(DefaultIcon, {
                    name: "upload",
                    color: "blue",
                    onClick: () => {
                      uploadControl.upload([file]);
                    }
                  });
                }

              }), createComponent(Match, {
                get when() {
                  return memo(() => progress() > 0, true)() && progress() < 100;
                },

                get children() {
                  return createComponent(DefaultIcon, {
                    name: "replay",
                    color: "orange",
                    spin: true,
                    onClick: () => uploadControl.cancel(file.sha)
                  });
                }

              }), createComponent(Match, {
                get when() {
                  return progress() === 100;
                },

                get children() {
                  return createComponent(DefaultIcon, {
                    name: "check",
                    color: "green"
                  });
                }

              })];
            }

          }), createComponent(DefaultIcon, {
            color: "red",
            name: "delete",
            onClick: () => {
              uploadControl.delete(file.sha);
            }
          })];
        }

      }), createComponent(Position, {
        full: true,
        bottom: "0",
        left: "0",
        "class": " ",
        inactive: true,

        get children() {
          const _el$3 = _tmpl$3.cloneNode(true);

          effect(() => _el$3.style.setProperty("width", progress() + '%'));

          return _el$3;
        }

      })];
    }

  });
};

const _tmpl$$2 = /*#__PURE__*/template(`<div></div>`, 2);
const UploadList = OriginComponent(props => {
  const {
    Files
  } = useContext(UploaderContext);
  return (() => {
    const _el$ = _tmpl$$2.cloneNode(true);

    const _ref$ = props.ref;
    typeof _ref$ === "function" ? _ref$(_el$) : props.ref = _el$;

    spread(_el$, () => extendsEvent(props), false, true);

    insert(_el$, createComponent(For, {
      get each() {
        return Files();
      },

      children: file => {
        const {
          uploadControl
        } = useContext(UploaderContext);
        const progress = reflect(() => uploadControl.getNotice(file.sha));
        return (props.children ?? DefaultUploadList)(file, progress, uploadControl);
      }
    }));

    effect(_p$ => {
      const _v$ = props.class('flex flex-col'),
            _v$2 = props.style;

      _v$ !== _p$._v$ && className(_el$, _p$._v$ = _v$);
      _p$._v$2 = style(_el$, _v$2, _p$._v$2);
      return _p$;
    }, {
      _v$: undefined,
      _v$2: undefined
    });

    return _el$;
  })();
});

const UploadWidget = OriginComponent(props => {
  const {
    isDragging,
    inputRef
  } = useContext(UploaderContext);
  return createComponent(Box, mergeProps(props, {
    get title() {
      return isDragging() ? '放下即可上传' : '拖拽或者点击上传文件';
    },

    get icon() {
      return createComponent(Button, {
        color: "blue",
        round: true,
        onClick: () => inputRef().click(),
        children: "\u4E0A\u4F20\u6587\u4EF6"
      });
    }

  }));
});

const UploadExplorer = OriginComponent(props => {
  const {
    Files
  } = useContext(UploaderContext);
  return createComponent(Explorer, mergeProps(props, {
    Files: Files,
    onOpenFile: file => {
      console.log(file);
    },
    FileExtra: file => createComponent(DefaultIcon, {
      name: "file_upload",
      color: "blue",
      onClick: () => {
        // TODO 上传未完成
        console.log(file);
      }
    }),
    FolderExtra: folder => createComponent(DefaultIcon, {
      name: "drive_folder_upload",
      color: "blue",
      onClick: () => {
        console.log(folder);
      }
    })
  }));
});

const _tmpl$$1 = /*#__PURE__*/template(`<div class=" h-full w-full  rounded-xl border-2 border-dashed border-slate-300  transition-colors duration-300 "></div>`, 2);

/** @zh  复合上传组件 */
const UploadPanel = OriginComponent(props => {
  const {
    Files,
    isDragging
  } = useContext(UploaderContext);
  return createComponent(Relative, mergeProps(props, {
    get ["class"]() {
      return props.class('cn-uploader  h-full w-full bg-slate-50 p-4');
    },

    get style() {
      return props.style;
    },

    ref(r$) {
      const _ref$ = props.ref;
      typeof _ref$ === "function" ? _ref$(r$) : props.ref = r$;
    }

  }, () => useDragUpload({
    isDragging,

    onEnd(files) {
      Files(i => [...i, ...files]);
    }

  }), {
    get children() {
      return [createComponent(Position, {
        full: true,
        "class": "p-4 ",
        top: "0",
        left: "0",
        inactive: true,

        get children() {
          const _el$ = _tmpl$$1.cloneNode(true);

          effect(() => _el$.classList.toggle("border-blue-600", !!isDragging()));

          return _el$;
        }

      }), memo(() => memo(() => Files().length === 0, true)() ? createComponent(UploadWidget, {}) : createComponent(UploadExplorer, {}))];
    }

  }));
});

/**
 * [js-sha256]{@link https://github.com/emn178/js-sha256}
 *
 * @version 0.9.0
 * @author Chen, Yi-Cyuan [emn178@gmail.com]
 * @copyright Chen, Yi-Cyuan 2014-2017
 * @license MIT
 * @description changed By Konghayao [dongzhongzhidong@qq.com]
 */

let ERROR = "input is invalid type";
let WINDOW = typeof window === "object";
let root = WINDOW ? window : {};

if (root.JS_SHA256_NO_WINDOW) {
  WINDOW = false;
}

let WEB_WORKER = !WINDOW && typeof self === "object";
let NODE_JS = !root.JS_SHA256_NO_NODE_JS && typeof process === "object" && process.versions && process.versions.node;

if (NODE_JS) {
  root = global;
} else if (WEB_WORKER) {
  root = self;
}

let ARRAY_BUFFER = !root.JS_SHA256_NO_ARRAY_BUFFER && typeof ArrayBuffer !== "undefined";
let HEX_CHARS = "0123456789abcdef".split("");
let EXTRA = [-2147483648, 8388608, 32768, 128];
let SHIFT = [24, 16, 8, 0];
let K = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2];
let OUTPUT_TYPES = ["hex", "array", "digest", "arrayBuffer"];
let blocks = [];

if (root.JS_SHA256_NO_NODE_JS || !Array.isArray) {
  Array.isArray = function (obj) {
    return Object.prototype.toString.call(obj) === "[object Array]";
  };
}

if (ARRAY_BUFFER && (root.JS_SHA256_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
  ArrayBuffer.isView = function (obj) {
    return typeof obj === "object" && obj.buffer && obj.buffer.constructor === ArrayBuffer;
  };
}

let createOutputMethod = function (outputType) {
  return function (message) {
    return new Sha256(true).update(message)[outputType]();
  };
};

let createMethod = function () {
  let method = createOutputMethod("hex");

  method.create = function () {
    return new Sha256();
  };

  method.update = function (message) {
    return method.create().update(message);
  };

  for (let i = 0; i < OUTPUT_TYPES.length; ++i) {
    let type = OUTPUT_TYPES[i];
    method[type] = createOutputMethod(type);
  }

  return method;
};

class Sha256 {
  constructor(sharedMemory) {
    if (sharedMemory) {
      blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
      this.blocks = blocks;
    } else {
      this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    } // 256


    this.h0 = 0x6a09e667;
    this.h1 = 0xbb67ae85;
    this.h2 = 0x3c6ef372;
    this.h3 = 0xa54ff53a;
    this.h4 = 0x510e527f;
    this.h5 = 0x9b05688c;
    this.h6 = 0x1f83d9ab;
    this.h7 = 0x5be0cd19;
    this.block = this.start = this.bytes = this.hBytes = 0;
    this.finalized = this.hashed = false;
    this.first = true;
  }

  update(message) {
    if (this.finalized) {
      return;
    }

    let notString,
        type = typeof message;

    if (type !== "string") {
      if (type === "object") {
        if (message === null) {
          throw new Error(ERROR);
        } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
          message = new Uint8Array(message);
        } else if (!Array.isArray(message)) {
          if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
            throw new Error(ERROR);
          }
        }
      } else {
        throw new Error(ERROR);
      }

      notString = true;
    }

    let code,
        index = 0,
        i,
        length = message.length,
        blocks = this.blocks;

    while (index < length) {
      if (this.hashed) {
        this.hashed = false;
        blocks[0] = this.block;
        blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
      }

      if (notString) {
        for (i = this.start; index < length && i < 64; ++index) {
          blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
        }
      } else {
        for (i = this.start; index < length && i < 64; ++index) {
          code = message.charCodeAt(index);

          if (code < 0x80) {
            blocks[i >> 2] |= code << SHIFT[i++ & 3];
          } else if (code < 0x800) {
            blocks[i >> 2] |= (0xc0 | code >> 6) << SHIFT[i++ & 3];
            blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
          } else if (code < 0xd800 || code >= 0xe000) {
            blocks[i >> 2] |= (0xe0 | code >> 12) << SHIFT[i++ & 3];
            blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
            blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
          } else {
            code = 0x10000 + ((code & 0x3ff) << 10 | message.charCodeAt(++index) & 0x3ff);
            blocks[i >> 2] |= (0xf0 | code >> 18) << SHIFT[i++ & 3];
            blocks[i >> 2] |= (0x80 | code >> 12 & 0x3f) << SHIFT[i++ & 3];
            blocks[i >> 2] |= (0x80 | code >> 6 & 0x3f) << SHIFT[i++ & 3];
            blocks[i >> 2] |= (0x80 | code & 0x3f) << SHIFT[i++ & 3];
          }
        }
      }

      this.lastByteIndex = i;
      this.bytes += i - this.start;

      if (i >= 64) {
        this.block = blocks[16];
        this.start = i - 64;
        this.hash();
        this.hashed = true;
      } else {
        this.start = i;
      }
    }

    if (this.bytes > 4294967295) {
      this.hBytes += this.bytes / 4294967296 << 0;
      this.bytes = this.bytes % 4294967296;
    }

    return this;
  }

  finalize() {
    if (this.finalized) {
      return;
    }

    this.finalized = true;
    let blocks = this.blocks,
        i = this.lastByteIndex;
    blocks[16] = this.block;
    blocks[i >> 2] |= EXTRA[i & 3];
    this.block = blocks[16];

    if (i >= 56) {
      if (!this.hashed) {
        this.hash();
      }

      blocks[0] = this.block;
      blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] = blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] = blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
    }

    blocks[14] = this.hBytes << 3 | this.bytes >>> 29;
    blocks[15] = this.bytes << 3;
    this.hash();
  }

  hash() {
    let a = this.h0,
        b = this.h1,
        c = this.h2,
        d = this.h3,
        e = this.h4,
        f = this.h5,
        g = this.h6,
        h = this.h7,
        blocks = this.blocks,
        j,
        s0,
        s1,
        maj,
        t1,
        t2,
        ch,
        ab,
        da,
        cd,
        bc;

    for (j = 16; j < 64; ++j) {
      // rightrotate
      t1 = blocks[j - 15];
      s0 = (t1 >>> 7 | t1 << 25) ^ (t1 >>> 18 | t1 << 14) ^ t1 >>> 3;
      t1 = blocks[j - 2];
      s1 = (t1 >>> 17 | t1 << 15) ^ (t1 >>> 19 | t1 << 13) ^ t1 >>> 10;
      blocks[j] = blocks[j - 16] + s0 + blocks[j - 7] + s1 << 0;
    }

    bc = b & c;

    for (j = 0; j < 64; j += 4) {
      if (this.first) {
        ab = 704751109;
        t1 = blocks[0] - 210244248;
        h = t1 - 1521486534 << 0;
        d = t1 + 143694565 << 0;
        this.first = false;
      } else {
        s0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
        s1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
        ab = a & b;
        maj = ab ^ a & c ^ bc;
        ch = e & f ^ ~e & g;
        t1 = h + s1 + ch + K[j] + blocks[j];
        t2 = s0 + maj;
        h = d + t1 << 0;
        d = t1 + t2 << 0;
      }

      s0 = (d >>> 2 | d << 30) ^ (d >>> 13 | d << 19) ^ (d >>> 22 | d << 10);
      s1 = (h >>> 6 | h << 26) ^ (h >>> 11 | h << 21) ^ (h >>> 25 | h << 7);
      da = d & a;
      maj = da ^ d & b ^ ab;
      ch = h & e ^ ~h & f;
      t1 = g + s1 + ch + K[j + 1] + blocks[j + 1];
      t2 = s0 + maj;
      g = c + t1 << 0;
      c = t1 + t2 << 0;
      s0 = (c >>> 2 | c << 30) ^ (c >>> 13 | c << 19) ^ (c >>> 22 | c << 10);
      s1 = (g >>> 6 | g << 26) ^ (g >>> 11 | g << 21) ^ (g >>> 25 | g << 7);
      cd = c & d;
      maj = cd ^ c & a ^ da;
      ch = g & h ^ ~g & e;
      t1 = f + s1 + ch + K[j + 2] + blocks[j + 2];
      t2 = s0 + maj;
      f = b + t1 << 0;
      b = t1 + t2 << 0;
      s0 = (b >>> 2 | b << 30) ^ (b >>> 13 | b << 19) ^ (b >>> 22 | b << 10);
      s1 = (f >>> 6 | f << 26) ^ (f >>> 11 | f << 21) ^ (f >>> 25 | f << 7);
      bc = b & c;
      maj = bc ^ b & d ^ cd;
      ch = f & g ^ ~f & h;
      t1 = e + s1 + ch + K[j + 3] + blocks[j + 3];
      t2 = s0 + maj;
      e = a + t1 << 0;
      a = t1 + t2 << 0;
    }

    this.h0 = this.h0 + a << 0;
    this.h1 = this.h1 + b << 0;
    this.h2 = this.h2 + c << 0;
    this.h3 = this.h3 + d << 0;
    this.h4 = this.h4 + e << 0;
    this.h5 = this.h5 + f << 0;
    this.h6 = this.h6 + g << 0;
    this.h7 = this.h7 + h << 0;
  }

  hex() {
    this.finalize();
    let h0 = this.h0,
        h1 = this.h1,
        h2 = this.h2,
        h3 = this.h3,
        h4 = this.h4,
        h5 = this.h5,
        h6 = this.h6,
        h7 = this.h7;
    let hex = HEX_CHARS[h0 >> 28 & 0x0f] + HEX_CHARS[h0 >> 24 & 0x0f] + HEX_CHARS[h0 >> 20 & 0x0f] + HEX_CHARS[h0 >> 16 & 0x0f] + HEX_CHARS[h0 >> 12 & 0x0f] + HEX_CHARS[h0 >> 8 & 0x0f] + HEX_CHARS[h0 >> 4 & 0x0f] + HEX_CHARS[h0 & 0x0f] + HEX_CHARS[h1 >> 28 & 0x0f] + HEX_CHARS[h1 >> 24 & 0x0f] + HEX_CHARS[h1 >> 20 & 0x0f] + HEX_CHARS[h1 >> 16 & 0x0f] + HEX_CHARS[h1 >> 12 & 0x0f] + HEX_CHARS[h1 >> 8 & 0x0f] + HEX_CHARS[h1 >> 4 & 0x0f] + HEX_CHARS[h1 & 0x0f] + HEX_CHARS[h2 >> 28 & 0x0f] + HEX_CHARS[h2 >> 24 & 0x0f] + HEX_CHARS[h2 >> 20 & 0x0f] + HEX_CHARS[h2 >> 16 & 0x0f] + HEX_CHARS[h2 >> 12 & 0x0f] + HEX_CHARS[h2 >> 8 & 0x0f] + HEX_CHARS[h2 >> 4 & 0x0f] + HEX_CHARS[h2 & 0x0f] + HEX_CHARS[h3 >> 28 & 0x0f] + HEX_CHARS[h3 >> 24 & 0x0f] + HEX_CHARS[h3 >> 20 & 0x0f] + HEX_CHARS[h3 >> 16 & 0x0f] + HEX_CHARS[h3 >> 12 & 0x0f] + HEX_CHARS[h3 >> 8 & 0x0f] + HEX_CHARS[h3 >> 4 & 0x0f] + HEX_CHARS[h3 & 0x0f] + HEX_CHARS[h4 >> 28 & 0x0f] + HEX_CHARS[h4 >> 24 & 0x0f] + HEX_CHARS[h4 >> 20 & 0x0f] + HEX_CHARS[h4 >> 16 & 0x0f] + HEX_CHARS[h4 >> 12 & 0x0f] + HEX_CHARS[h4 >> 8 & 0x0f] + HEX_CHARS[h4 >> 4 & 0x0f] + HEX_CHARS[h4 & 0x0f] + HEX_CHARS[h5 >> 28 & 0x0f] + HEX_CHARS[h5 >> 24 & 0x0f] + HEX_CHARS[h5 >> 20 & 0x0f] + HEX_CHARS[h5 >> 16 & 0x0f] + HEX_CHARS[h5 >> 12 & 0x0f] + HEX_CHARS[h5 >> 8 & 0x0f] + HEX_CHARS[h5 >> 4 & 0x0f] + HEX_CHARS[h5 & 0x0f] + HEX_CHARS[h6 >> 28 & 0x0f] + HEX_CHARS[h6 >> 24 & 0x0f] + HEX_CHARS[h6 >> 20 & 0x0f] + HEX_CHARS[h6 >> 16 & 0x0f] + HEX_CHARS[h6 >> 12 & 0x0f] + HEX_CHARS[h6 >> 8 & 0x0f] + HEX_CHARS[h6 >> 4 & 0x0f] + HEX_CHARS[h6 & 0x0f];
    hex += HEX_CHARS[h7 >> 28 & 0x0f] + HEX_CHARS[h7 >> 24 & 0x0f] + HEX_CHARS[h7 >> 20 & 0x0f] + HEX_CHARS[h7 >> 16 & 0x0f] + HEX_CHARS[h7 >> 12 & 0x0f] + HEX_CHARS[h7 >> 8 & 0x0f] + HEX_CHARS[h7 >> 4 & 0x0f] + HEX_CHARS[h7 & 0x0f];
    return hex;
  }

  digest() {
    this.finalize();
    let h0 = this.h0,
        h1 = this.h1,
        h2 = this.h2,
        h3 = this.h3,
        h4 = this.h4,
        h5 = this.h5,
        h6 = this.h6,
        h7 = this.h7;
    let arr = [h0 >> 24 & 0xff, h0 >> 16 & 0xff, h0 >> 8 & 0xff, h0 & 0xff, h1 >> 24 & 0xff, h1 >> 16 & 0xff, h1 >> 8 & 0xff, h1 & 0xff, h2 >> 24 & 0xff, h2 >> 16 & 0xff, h2 >> 8 & 0xff, h2 & 0xff, h3 >> 24 & 0xff, h3 >> 16 & 0xff, h3 >> 8 & 0xff, h3 & 0xff, h4 >> 24 & 0xff, h4 >> 16 & 0xff, h4 >> 8 & 0xff, h4 & 0xff, h5 >> 24 & 0xff, h5 >> 16 & 0xff, h5 >> 8 & 0xff, h5 & 0xff, h6 >> 24 & 0xff, h6 >> 16 & 0xff, h6 >> 8 & 0xff, h6 & 0xff];
    arr.push(h7 >> 24 & 0xff, h7 >> 16 & 0xff, h7 >> 8 & 0xff, h7 & 0xff);
    return arr;
  }

  arrayBuffer() {
    this.finalize();
    let buffer = new ArrayBuffer(32);
    let dataView = new DataView(buffer);
    dataView.setUint32(0, this.h0);
    dataView.setUint32(4, this.h1);
    dataView.setUint32(8, this.h2);
    dataView.setUint32(12, this.h3);
    dataView.setUint32(16, this.h4);
    dataView.setUint32(20, this.h5);
    dataView.setUint32(24, this.h6);
    dataView.setUint32(28, this.h7);
    return buffer;
  }

}

Sha256.prototype.toString = Sha256.prototype.hex;
Sha256.prototype.array = Sha256.prototype.digest;

const sha256 = createMethod();

/** 判断是否 accept file
 * @link https://github.com/arco-design/arco-design/blob/main/components/Upload/util.tsx#L3-L43
 */
const isAcceptFile = (file, accept) => {
  if (accept && file) {
    const accepts = accept.split(',').map(x => x.trim()).filter(x => x);
    const fileExtension = file.name.indexOf('.') > -1 ? `.${file.name.split('.').pop()}` : '';
    return accepts.some(type => {
      const typeText = type && type.toLowerCase();
      const fileType = (file.type || '').toLowerCase();

      if (typeText === fileType) {
        // 类似excel文件这种
        // 比如application/vnd.ms-excel和application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
        // 本身就带有.字符的，不能走下面的.jpg等文件扩展名判断处理
        // 所以优先对比input的accept类型和文件对象的type值
        return true;
      } // */*,*  之类的所有类型


      if (/^\*(\/\*)?$/.test(typeText)) {
        return true;
      }

      if (/\/\*/.test(typeText)) {
        // image/* 这种通配的形式处理
        return fileType.replace(/\/.*$/, '') === typeText.replace(/\/.*$/, '');
      }

      if (/\..*/.test(typeText)) {
        // .jpg 等后缀名
        let suffixList = [typeText]; // accept=".jpg", jpeg后缀类型同样可以上传，反之亦然

        if (typeText === '.jpg' || typeText === '.jpeg') {
          suffixList = ['.jpg', '.jpeg'];
        }

        return suffixList.indexOf(fileExtension) > -1;
      }

      return false;
    });
  }

  return !!file;
};

class UploadController {
  uploadState = {};

  constructor(props) {
    Object.assign(this, props);
  }

  mode = 'replace';
  multiple = false;
  limit = Infinity;

  /** 计算 sha 值，并进行缓存 */
  async calcSha(file) {
    if (file.sha) return file.sha;
    return sha256(await file.arrayBuffer());
  }
  /** 在存储库中加入一个键值对 */


  async createSlot(file) {
    const sha = await this.calcSha(file);

    if (!this.uploadState[sha]) {
      this.uploadState[sha] = atom(0);
      file.sha = sha;
      file.progress = this.uploadState[sha];
    }

    return this.uploadState[sha];
  }

  getNotice(sha) {
    const data = this.uploadState[sha];
    return data ? data() : -1;
  }

  async addFiles(files) {
    const diffNumber = this.limit - this.Files().length;
    if (diffNumber <= 0) return false;

    if (this.accept) {
      files = files.filter(i => isAcceptFile(i, this.accept));
    }

    if (!this.multiple) {
      files = [files[0]];
    }

    return Promise.all(files.filter(i => i).map(i => {
      return this.createSlot(i);
    })).then(res => {
      if (this.mode === 'add') {
        this.Files(i => [...i, ...files]);
      } else {
        this.Files(files);
      }

      return res;
    });
  }

  cancelChannel = mitt();

  async upload(files) {
    const notify = (data, sha) => {
      if (sha) {
        this.uploadState[sha](data);
      } else {
        files.forEach(i => {
          this.calcSha(i).then(sha => {
            this.uploadState[sha](data);
          });
        });
      }
    }; // 没错，置 1 表示开始


    notify(1);
    return this.uploading(notify, files, cb => {
      console.log('挂载 cancel');
      files.forEach(i => {
        this.cancelChannel.on(i.sha, () => cb(i));
      });
    }).then(i => {
      notify(100);
      return i;
    });
  }

  delete(sha) {
    const p = this.getNotice(sha);

    if (p > 0 && p < 100) {
      // 先暂停，再次点击关闭
      this.cancel(sha);
    } else {
      this.Files(i => i.filter(i => i.sha !== sha));
      delete this.uploadState[sha];
    }
  }

  cancel(sha) {
    this.uploadState[sha](-1);
    this.cancelChannel.emit(sha);
  }

}

const _tmpl$ = /*#__PURE__*/template(`<input class="hidden" type="file">`, 1);

/** 这个并没有实质上的 DOM，故不需要进行 OriginComponent */
const UploaderRoot = props => {
  // 直接传递 props 会报 BUG
  const uploadControl = new UploadController({
    accept: props.accept,
    limit: props.limit ?? Infinity,
    multiple: props.multiple ?? false,
    mode: props.mode ?? 'replace',
    Files: props.Files,
    uploading: props.uploading
  });
  let inputRef = atom(null);
  console.log('root');
  return createComponent(UploaderContext.Provider, {
    get value() {
      return {
        inputRef,
        Files: props.Files,
        isDragging: atom(false),
        uploadControl
      };
    },

    get children() {
      return [(() => {
        const _el$ = _tmpl$.cloneNode(true);

        _el$.addEventListener("change", async e => {
          const data = [...e.target.files];
          await uploadControl.addFiles(data.map(i => {
            // @ts-ignore
            i.fullPath = '/' + i.name;
            return i;
          }));
        });

        const _ref$ = inputRef;
        typeof _ref$ === "function" ? _ref$(_el$) : inputRef = _el$;

        effect(_p$ => {
          const _v$ = props.multiple,
                _v$2 = props.accept;
          _v$ !== _p$._v$ && (_el$.multiple = _p$._v$ = _v$);
          _v$2 !== _p$._v$2 && setAttribute(_el$, "accept", _p$._v$2 = _v$2);
          return _p$;
        }, {
          _v$: undefined,
          _v$2: undefined
        });

        return _el$;
      })(), memo(() => props.children)];
    }

  });
};

export { SimpleUploader, UploadList, UploadPanel, UploaderRoot, UploadingContext };
