/*
loader.js:负责对资源的加载

 */
"use strict";

function loader() {
  this._init();
}

loader.prototype._init = function () {};

////// 设置加载进度条进度 //////
loader.prototype._setStartProgressVal = function (val) {
  core.dom.startTopProgress.style.width = val + "%";
};

////// 设置加载进度条提示文字 //////
loader.prototype._setStartLoadTipText = function (text) {
  core.dom.startTopLoadTips.innerText = text;
};

loader.prototype._load = function (callback) {
  this._loadMusics();
  if (main.useCompress) {
    this._load_async(callback);
  } else {
    this._load_sync(callback);
  }
};

loader.prototype._load_sync = function (callback) {
  this._loadAnimates_sync();
  this._loadSounds_sync();
  core.loader._loadMaterials_sync(function () {
    core.loader._loadExtraImages_sync(function () {
      core.loader._loadAutotiles_sync(function () {
        core.loader._loadTilesets_sync(callback);
      });
    });
  });
};

loader.prototype._load_async = function (callback) {
  core.loader._setStartLoadTipText("正在加载资源文件...");
  const all = {};

  const _makeOnProgress = function (name) {
    if (!all[name]) all[name] = { loaded: 0, total: 0, finished: false };
    return (loaded, total) => {
      all[name].loaded = loaded;
      all[name].total = total;
      let allLoaded = 0,
        allTotal = 0;
      for (const one of Object.values(all)) {
        allLoaded += one.loaded;
        allTotal += one.total;
      }
      console.log(allLoaded.allTotal);
      if (allTotal > 0) {
        if (allLoaded == allTotal) {
          core.loader._setStartLoadTipText("正在处理资源文件... 请稍候...");
        } else {
          core.loader._setStartLoadTipText(
            "正在加载资源文件... " +
              core.formatSize(allLoaded) +
              " / " +
              core.formatSize(allTotal) +
              " (" +
              ((allLoaded / allTotal) * 100).toFixed(2) +
              "%)"
          );
        }
        core.loader._setStartProgressVal((allLoaded / allTotal) * 100);
      }
    };
  };
  const _makeOnFinished = function (name) {
    return () => {
      setTimeout(() => {
        all[name].finished = true;
        for (var one in all) {
          if (!all[one].finished) return;
        }
        callback();
      });
    };
  };

  if (main.splitChunkMap) {
    this._loadAnimates_chunked(
      main.splitChunkMap.animates,
      _makeOnProgress,
      _makeOnFinished
    );
    this._loadSounds_chunked(
      main.splitChunkMap.sounds,
      _makeOnProgress,
      _makeOnFinished
    );
    this._loadMaterials_chunked(
      main.splitChunkMap.materials,
      _makeOnProgress,
      _makeOnFinished
    );
    this._loadExtraImages_chunked(
      main.splitChunkMap.images,
      _makeOnProgress,
      _makeOnFinished
    );
    this._loadAutotiles_chunked(
      main.splitChunkMap.autotiles,
      _makeOnProgress,
      _makeOnFinished
    );
    this._loadTilesets_chunked(
      main.splitChunkMap.tilesets,
      _makeOnProgress,
      _makeOnFinished
    );
  } else {
    this._loadAnimates_async(
      _makeOnProgress("animates"),
      _makeOnFinished("animates")
    );
    this._loadSounds_async(
      _makeOnProgress("sounds"),
      _makeOnFinished("sounds")
    );
    this._loadMaterials_async(
      _makeOnProgress("materials"),
      _makeOnFinished("materials")
    );
    this._loadExtraImages_async(
      _makeOnProgress("images"),
      _makeOnFinished("images")
    );
    this._loadAutotiles_async(
      _makeOnProgress("autotiles"),
      _makeOnFinished("autotiles")
    );
    this._loadTilesets_async(
      _makeOnProgress("tilesets"),
      _makeOnFinished("tilesets")
    );
  }
};

// ----- 加载资源文件 ------ //

loader.prototype._loadMaterials_sync = function (callback) {
  this._setStartLoadTipText("正在加载资源文件...");
  this.loadImages(
    "materials",
    core.materials,
    core.material.images,
    function () {
      core.loader._loadMaterials_afterLoad();
      callback();
    }
  );
};

loader.prototype._loadMaterials_async = function (onprogress, onfinished) {
  this.loadImagesFromZip(
    "project/materials/materials.h5data",
    core.materials,
    core.material.images,
    onprogress,
    function () {
      core.loader._loadMaterials_afterLoad();
      onfinished();
    }
  );
};

loader.prototype._loadMaterials_chunked = async function (
  chunks,
  makeOnProgress,
  makeOnFinished
) {
  await this._loadImagesFromChunks(
    chunks,
    core.materials,
    core.material.images,
    makeOnProgress,
    makeOnFinished
  );
  core.loader._loadMaterials_afterLoad();
};

loader.prototype._loadMaterials_afterLoad = function () {
  const images = core.splitImage(core.material.images["icons"]);
  for (let key in core.statusBar.icons) {
    if (typeof core.statusBar.icons[key] == "number") {
      core.statusBar.icons[key] = images[core.statusBar.icons[key]];
      if (core.statusBar.image[key] != null)
        core.statusBar.image[key].src = core.statusBar.icons[key].src;
    }
  }
};

// ------ 加载使用的图片 ------ //

loader.prototype._loadExtraImages_sync = function (callback) {
  core.material.images.images = {};
  this._setStartLoadTipText("正在加载图片文件...");
  core.loadImages("images", core.images, core.material.images.images, callback);
};

loader.prototype._loadExtraImages_async = function (onprogress, onfinished) {
  core.material.images.images = {};

  // Check .gif
  const gifs = images.filter(function (name) {
    return name.toLowerCase().endsWith(".gif");
  });
  // gif没有被压缩在zip中,延迟加载...
  this._loadExtraImages_loadLazy(gifs);

  images = images.filter((name) => !name.toLowerCase().endsWith(".gif"));

  this.loadImagesFromZip(
    "project/images/images.h5data",
    images,
    core.material.images.images,
    onprogress,
    onfinished
  );
};

loader.prototype._loadExtraImages_chunked = function (
  chunks,
  makeOnProgress,
  makeOnFinished
) {
  core.material.images.images = {};
  let images = core.images;

  // Check .gif
  const gifs = images.filter(function (name) {
    return name.toLowerCase().endsWith(".gif");
  });
  // gif没有被压缩在zip中,延迟加载...
  this._loadExtraImages_loadLazy(gifs);

  images = images.filter((name) => !name.toLowerCase().endsWith(".gif"));

  this._loadImagesFromChunks(
    chunks,
    images,
    core.material.images.images,
    makeOnProgress,
    makeOnFinished
  );
};

loader.prototype._loadExtraImages_loadLazy = function (list) {
  list.forEach(function (gif) {
    this.loadImage("images", gif, (id, image) => {
      if (image != null) {
        core.material.images.images[gif] = image;
      }
    });
  }, this);
};

// ------ 加载自动元件 ------ //

loader.prototype._loadAutotiles_sync = function (callback) {
  core.material.images.autotile = {};
  var keys = Object.keys(core.material.icons.autotile);
  var autotiles = {};

  this._setStartLoadTipText("正在加载自动元件...");
  this.loadImages("autotiles", keys, autotiles, function () {
    core.loader._loadAutotiles_afterLoad(keys, autotiles);
    callback();
  });
};

loader.prototype._loadAutotiles_async = function (onprogress, onfinished) {
  core.material.images.autotile = {};
  var keys = Object.keys(core.material.icons.autotile);
  var autotiles = {};

  this.loadImagesFromZip(
    "project/autotiles/autotiles.h5data",
    keys,
    autotiles,
    onprogress,
    function () {
      core.loader._loadAutotiles_afterLoad(keys, autotiles);
      onfinished();
    }
  );
};

loader.prototype._loadAutotiles_chunked = async function (
  chunks,
  makeOnProgress,
  makeOnFinished
) {
  core.material.images.autotile = {};
  const keys = Object.keys(core.material.icons.autotile);
  const autotiles = {};

  await this._loadImagesFromChunks(
    chunks,
    keys,
    autotiles,
    makeOnProgress,
    makeOnFinished
  );
  core.loader._loadAutotiles_afterLoad(keys, autotiles);
};

loader.prototype._loadAutotiles_afterLoad = function (keys, autotiles) {
  // autotile需要保证顺序
  keys.forEach(function (v) {
    core.material.images.autotile[v] = autotiles[v];
  });

  setTimeout(function () {
    core.maps._makeAutotileEdges();
  });
};

// ------ 加载额外素材 ------ //

loader.prototype._loadTilesets_sync = function (callback) {
  core.material.images.tilesets = {};
  this._setStartLoadTipText("正在加载额外素材...");
  this.loadImages(
    "tilesets",
    core.tilesets,
    core.material.images.tilesets,
    function () {
      core.loader._loadTilesets_afterLoad();
      callback();
    }
  );
};

loader.prototype._loadTilesets_async = function (onprogress, onfinished) {
  core.material.images.tilesets = {};
  this.loadImagesFromZip(
    "project/tilesets/tilesets.h5data",
    core.tilesets,
    core.material.images.tilesets,
    onprogress,
    function () {
      core.loader._loadTilesets_afterLoad();
      onfinished();
    }
  );
};

loader.prototype._loadTilesets_chunked = async function (
  chunks,
  makeOnProgress,
  makeOnFinished
) {
  core.material.images.tilesets = {};
  await this._loadImagesFromChunks(
    chunks,
    core.tilesets,
    core.material.images.tilesets,
    makeOnProgress,
    makeOnFinished
  );
  core.loader._loadTilesets_afterLoad();
};

loader.prototype._loadTilesets_afterLoad = function () {
  // 检查宽高是32倍数,如果出错在控制台报错
  for (var imgName in core.material.images.tilesets) {
    var img = core.material.images.tilesets[imgName];
    if (img.width % 32 != 0 || img.height % 32 != 0) {
      console.warn("警告!" + imgName + "的宽或高不是32的倍数!");
    }
    if (img.width * img.height > 32 * 32 * 3000) {
      console.warn("警告!" + imgName + "上的图块素材个数大于3000!");
    }
  }
};

// ------ 实际加载一系列图片 ------ //

loader.prototype.loadImages = function (dir, names, toSave, callback) {
  if (!names || names.length == 0) {
    if (callback) callback();
    return;
  }
  var items = 0;
  for (var i = 0; i < names.length; i++) {
    this.loadImage(dir, names[i], function (id, image) {
      core.loader._setStartLoadTipText("正在加载图片 " + id + "...");
      if (toSave[id] !== undefined) {
        if (image != null) toSave[id] = image;
        return;
      }
      toSave[id] = image;
      items++;
      core.loader._setStartProgressVal(items * (100 / names.length));
      if (items == names.length) {
        if (callback) callback();
      }
    });
  }
};

loader.prototype.loadImage = function (dir, imgName, callback) {
  try {
    var name = imgName;
    if (name.indexOf(".") < 0) name = name + ".png";
    var image = new Image();
    image.onload = function () {
      image.setAttribute("_width", image.width);
      image.setAttribute("_height", image.height);
      callback(imgName, image);
    };
    image.onerror = function () {
      callback(imgName, null);
    };
    image.src = "project/" + dir + "/" + name + "?v=" + main.version;
    if (name.endsWith(".gif")) callback(imgName, null);
  } catch (e) {
    console.error(e);
  }
};

// ------ 从zip中加载一系列图片 ------ //

loader.prototype.loadImagesFromZip = function (
  url,
  names,
  toSave,
  onprogress,
  onfinished
) {
  if (!names || names.length == 0) {
    if (onfinished) onfinished();
    return;
  }

  core.unzip(
    url + "?v=" + main.version,
    function (data) {
      var cnt = 1;
      names.forEach(function (name) {
        var imgName = name;
        if (imgName.indexOf(".") < 0) imgName += ".png";
        if (imgName in data) {
          var img = new Image();
          var url = URL.createObjectURL(data[imgName]);
          cnt++;
          img.onload = function () {
            cnt--;
            URL.revokeObjectURL(url);
            img.setAttribute("_width", img.width);
            img.setAttribute("_height", img.height);
            if (cnt == 0 && onfinished) onfinished();
          };
          img.src = url;
          toSave[name] = img;
        }
      });
      cnt--;
      if (cnt == 0 && onfinished) onfinished();
    },
    null,
    false,
    onprogress
  );
};

loader.prototype._loadImagesFromChunks = async function (
  chunks,
  names,
  toSave,
  makeOnProgress,
  makeOnFinished
) {
  await Promise.all(
    chunks.map((chunk) => {
      const onfinished = makeOnFinished(chunk);
      const onprogress = makeOnProgress(chunk);
      return new Promise((resolve) => {
        this.loadImagesFromZip(chunk, names, toSave, onprogress, () => {
          onfinished();
          resolve();
        });
      });
    })
  );
};

// ------ 加载动画文件 ------ //

loader.prototype._loadAnimates_sync = function () {
  this._setStartLoadTipText("正在加载动画文件...");

  if (main.supportBunch) {
    if (core.animates.length > 0) {
      core.http(
        "GET",
        "__all_animates__?v=" + main.version + "&id=" + core.animates.join(","),
        null,
        function (content) {
          var u = content.split("@@@~~~###~~~@@@");
          for (var i = 0; i < core.animates.length; ++i) {
            if (u[i] != "") {
              core.material.animates[core.animates[i]] =
                core.loader._loadAnimate(u[i]);
            } else {
              console.error("无法找到动画文件" + core.animates[i] + "!");
            }
          }
        },
        "text/plain; charset=x-user-defined"
      );
    }
    return;
  }

  core.animates.forEach(function (t) {
    core.http(
      "GET",
      "project/animates/" + t + ".animate?v=" + main.version,
      null,
      function (content) {
        core.material.animates[t] = core.loader._loadAnimate(content);
      },
      function (e) {
        console.error(e);
        core.material.animates[t] = null;
      },
      "text/plain; charset=x-user-defined"
    );
  });
};

loader.prototype._loadAnimates_async = function (onprogress, onfinished) {
  this._loadFileFromZip(
    "project/animates/animates.h5data",
    this._saveAnimate,
    true,
    onprogress,
    onfinished
  );
};

loader.prototype._loadAnimates_chunked = function (
  chunks,
  makeOnProgress,
  makeOnFinished
) {
  this._loadFileFromChunks(
    chunks,
    this._saveAnimate,
    true,
    makeOnProgress,
    makeOnFinished
  );
};

loader.prototype._saveAnimate = function (animates, onfinished) {
  for (var name in animates) {
    if (name.endsWith(".animate")) {
      var t = name.substring(0, name.length - 8);
      if (core.animates.indexOf(t) >= 0)
        core.material.animates[t] = core.loader._loadAnimate(animates[name]);
    }
  }
  onfinished();
};

loader.prototype._loadAnimate = function (content) {
  try {
    content = JSON.parse(content);
    var data = {};
    data.ratio = content.ratio;
    data.se = content.se;
    data.pitch = content.pitch;
    data.images = [];
    content.bitmaps.forEach(function (t2) {
      if (!t2) {
        data.images.push(null);
      } else {
        try {
          var image = new Image();
          image.src = t2;
          data.images.push(image);
        } catch (e) {
          console.error(e);
          data.images.push(null);
        }
      }
    });
    data.frame = content.frame_max;
    data.frames = [];
    content.frames.forEach(function (t2) {
      var info = [];
      t2.forEach(function (t3) {
        info.push({
          index: t3[0],
          x: t3[1],
          y: t3[2],
          zoom: t3[3],
          opacity: t3[4],
          mirror: t3[5] || 0,
          angle: t3[6] || 0,
        });
      });
      data.frames.push(info);
    });
    return data;
  } catch (e) {
    console.error(e);
    return null;
  }
};

// ------ 加载音乐和音效 ------ //

loader.prototype._loadMusics = function () {
  core.bgms.forEach(function (t) {
    core.loader.loadOneMusic(t);
  });
  // 直接开始播放
  core.playBgm(main.startBgm);
};

loader.prototype._loadSounds_sync = function () {
  this._setStartLoadTipText("正在加载音效文件...");
  core.sounds.forEach(function (t) {
    core.loader.loadOneSound(t);
  });
};

loader.prototype._loadSounds_async = function (onprogress, onfinished) {
  this._loadFileFromZip(
    "project/sounds/sounds.h5data",
    this._saveSounds,
    false,
    onprogress,
    onfinished
  );
};

loader.prototype._loadSounds_chunked = function (
  chunks,
  makeOnProgress,
  makeOnFinished
) {
  this._loadFileFromChunks(
    chunks,
    this._saveSounds,
    false,
    makeOnProgress,
    makeOnFinished
  );
};

loader.prototype._saveSounds = function (data, onfinished) {
  // 延迟解析
  setTimeout(function () {
    for (var name in data) {
      if (core.sounds.indexOf(name) >= 0) {
        core.loader._loadOneSound_decodeData(name, data[name]);
      }
    }
    onfinished();
  });
};

loader.prototype.loadOneMusic = function (name) {
  /* var music = new Audio();
  music.preload = "none";
  if (main.bgmRemote)
    music.src = main.bgmRemoteRoot + core.firstData.name + "/" + name;
  else music.src = "project/bgms/" + name;
  music.loop = "loop";
  core.material.bgms[name] = music;*/
};

loader.prototype.loadOneSound = function (name) {
  core.http(
    "GET",
    "project/sounds/" + name + "?v=" + main.version,
    null,
    function (data) {
      core.loader._loadOneSound_decodeData(name, data);
    },
    function (e) {
      console.error(e);
      core.material.sounds[name] = null;
    },
    null,
    "arraybuffer"
  );
};

loader.prototype._loadOneSound_decodeData = function (name, data) {
  if (data instanceof Blob) {
    var blobReader = new zip.BlobReader(data);
    blobReader.init(function () {
      blobReader.readUint8Array(0, blobReader.size, function (uint8) {
        core.loader._loadOneSound_decodeData(name, uint8.buffer);
      });
    });
    return;
  }
  try {
    core.musicStatus.audioContext.decodeAudioData(
      data,
      function (buffer) {
        core.material.sounds[name] = buffer;
      },
      function (e) {
        console.error(e);
        core.material.sounds[name] = null;
      }
    );
  } catch (e) {
    console.error(e);
    core.material.sounds[name] = null;
  }
};

loader.prototype.loadBgm = function (name) {
  name = core.getMappedName(name);
  if (!core.material.bgms[name]) return;
  // 如果没开启音乐,则不预加载
  if (!core.musicStatus.bgmStatus) return;
  // 是否已经预加载过
  var index = core.musicStatus.cachedBgms.indexOf(name);
  if (index >= 0) {
    core.musicStatus.cachedBgms.splice(index, 1);
  } else {
    // 预加载BGM
    this._preloadBgm(core.material.bgms[name]);
    // core.material.bgms[name].load();
    // 清理尾巴
    if (core.musicStatus.cachedBgms.length == core.musicStatus.cachedBgmCount) {
      this.freeBgm(core.musicStatus.cachedBgms.pop());
    }
  }
  // 移动到缓存最前方
  core.musicStatus.cachedBgms.unshift(name);
};

loader.prototype._preloadBgm = function (bgm) {
  bgm.volume = 0;
  bgm.play();
};

loader.prototype.freeBgm = function (name) {
  name = core.getMappedName(name);
  if (!core.material.bgms[name]) return;
  // 从cachedBgms中删除
  core.musicStatus.cachedBgms = core.musicStatus.cachedBgms.filter(function (
    t
  ) {
    return t != name;
  });
  // 清掉缓存
  core.material.bgms[name].removeAttribute("src");
  core.material.bgms[name].load();
  core.material.bgms[name] = null;
  if (name == core.musicStatus.playingBgm) {
    core.musicStatus.playingBgm = null;
  }
  // 三秒后重新加载
  setTimeout(function () {
    core.loader.loadOneMusic(name);
  }, 3000);
};

loader.prototype._loadFileFromZip = function (
  url,
  save,
  convertToText,
  onprogress,
  onfinished
) {
  core.unzip(
    url + "?v=" + main.version,
    function (data) {
      save(data, onfinished);
    },
    null,
    convertToText,
    onprogress
  );
};

loader.prototype._loadFileFromChunks = async function (
  chunks,
  save,
  convertToText,
  makeOnProgress,
  makeOnFinished
) {
  await Promise.all(
    chunks.map((chunk) => {
      const onfinished = makeOnFinished(chunk);
      const onprogress = makeOnProgress(chunk);
      return new Promise((resolve) => {
        this._loadFileFromZip(chunk, save, convertToText, onprogress, () => {
          onfinished();
          resolve();
        });
      });
    })
  );
};