const { spawn } = require("child_process");
const fs = require("fs");
const compressing = require('compressing');
const { v4, parse } = require('uuid');
const { Base64 } = require('js-base64');
const { copyDir, removeDir, fix2 } = require("./utils");

async function buildForm() {
  const paths = fs.readdirSync('form');

  if (!paths.includes('tsconfig.json')) {
    await copyDir('form', 'dist/form');
    return;
  }

  if (process.platform == 'win32') {
    await execCmd('npm.cmd', ['install'], 'form');
  } else {
    await execCmd('npm', ['install'], 'form');
  }

  if (process.platform == 'win32') {
    await execCmd('npm.cmd', ['run', 'publish'], 'form');
  } else {
    await execCmd('npm', ['run', 'publish'], 'form');
  }

  await compressing.zip.uncompress('form/publish/form.zip', 'dist/form');
}

function execCmd(cmd, params, path) {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      cmd,
      params,
      { cwd: path }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
}


let creatorBasePath = 'CocosCreator';
if (process.platform !== 'win32') {
  creatorBasePath = "/Applications/CocosCreator/Creator/2.4.5/CocosCreator.app/Contents/MacOS/CocosCreator";
}
const buildCocos = function (args) {
  return new Promise((resolve, reject) => {
    const buffer = spawn(
      creatorBasePath,
      args,
      { cwd: '.' }
    );

    buffer.stdout.on('data', (data) => {
      console.log(`stdout: ${data}`);
    });

    buffer.stderr.on('data', (data) => {
      console.error(`stderr: ${data}`);
    });

    buffer.on('close', (code) => {
      console.log(`child process exited with code ${code}`);
      resolve();
    });
  });
};

function getReleaseFileName(projectName) {
  let date = new Date();
  let fileName = `${projectName}_${date.getFullYear()}${fix2(date.getMonth() + 1)}${fix2(date.getDate())} `;
  fileName += `${fix2(date.getHours())}-${fix2(date.getMinutes())}-${fix2(date.getSeconds())}`;
  return fileName;
}

function getFolderName(path) {
  let folderName = '';
  fs.readdirSync(path).find(fileName => {
    const st = fs.statSync(`${path}/${fileName}`);
    if (st.isDirectory()) {
      folderName = fileName;
    }
  });
  return folderName;
}
function editFolderMeta(path, folderName, isBundle) {
  const metaPath = `${path}/${folderName}.meta`;
  const metaDataStr = fs.readFileSync(metaPath);
  const metaData = JSON.parse(metaDataStr);
  metaData.isBundle = isBundle;
  metaData.isRemoteBundle = {
    ios: isBundle,
    android: isBundle
  };
  fs.writeFileSync(metaPath, JSON.stringify(metaData));
}

async function buildWebDesktop() {
  const args = ['--path', './', '--build', 'platform=web-desktop;debug=true', '--force'];
  await buildCocos(args);
}

async function buildAndroidBundle() {
  const args = ['--path', './', '--build', "platform=ios;debug=false;md5Cache=true;buildPath=build_android;appABIs=['armeabi-v7a','x86','x86_64','arm64-v8a'];encryptJs=true;xxteaKey=6bbfce23-28b4-4a;zipCompressJs=true", '--force'];
  await buildCocos(args);
}

async function buildIosBundle() {
  const args = ['--path', './', '--build', "platform=ios;debug=false;md5Cache=true;buildPath=build_ios;encryptJs=true;xxteaKey=6bbfce23-28b4-4a;zipCompressJs=true", '--force'];
  await buildCocos(args);
}

async function buildWebBundle() {
  const args = ['--path', './', '--build', "platform=web-desktop;debug=false;buildPath=build_web_desktop", '--force'];
  await buildCocos(args);
}

function createConfigFile(projectName, type) {
  let iosVersion = "";
  let androidVersion = "";
  if (!type) {
    const androidPaths = fs.readdirSync(`dist/android/${projectName}`);
    const androidConfigFileName = androidPaths.find(path => path.indexOf('config') == 0);
    androidVersion = androidConfigFileName.split('.')[1];
    const iosPaths = fs.readdirSync(`dist/ios/${projectName}`);
    const iosConfigFileName = iosPaths.find(path => path.indexOf('config') == 0);
    iosVersion = iosConfigFileName.split('.')[1];
  } else {
    if (type == "android") {
      const androidPaths = fs.readdirSync(`dist/android/${projectName}`);
      const androidConfigFileName = androidPaths.find(path => path.indexOf('config') == 0);
      androidVersion = androidConfigFileName.split('.')[1];
    } else {
      const iosPaths = fs.readdirSync(`dist/ios/${projectName}`);
      const iosConfigFileName = iosPaths.find(path => path.indexOf('config') == 0);
      iosVersion = iosConfigFileName.split('.')[1];
    }
  }

  const config = {
    "ios": {
      "sceneName": projectName,
      "version": iosVersion
    },
    "android": {
      "sceneName": projectName,
      "version": androidVersion
    }
  }

  fs.writeFileSync('dist/config.json', JSON.stringify(config));
}

function compressAll(projectName) {
  const tarStream = new compressing.zip.Stream();
  tarStream.addEntry('dist/play');
  tarStream.addEntry('dist/form');
  tarStream.addEntry('dist/ios');
  tarStream.addEntry('dist/android');
  tarStream.addEntry('dist/web_desktop');
  tarStream.addEntry('dist/config.json');
  const destStream = fs.createWriteStream(`publish/${getReleaseFileName(projectName)}.zip`);
  tarStream.pipe(destStream);
}

function build_check() {
  const dirNames = process.cwd().split(/\/|\\/);
  const projectName = dirNames[dirNames.length - 1];
  const path = 'assets'
  let folderName = '';
  fs.readdirSync(path).find(fileName => {
    const st = fs.statSync(`${path}/${fileName}`);
    if (st.isDirectory()) {
      folderName = fileName;
    }
  });

  if (projectName != folderName) {
    throw (`项目名(${projectName})与bundle文件夹名(${folderName})不相同`);
  }
  let same = false;
  const files = fs.readdirSync(`${path}/${folderName}/scene`);
  files.forEach(fileName => {
    fileName.split('.').forEach((str, idx, arr) => {
      if (str == 'fire') {
        const sceneName = arr[idx - 1];
        if (folderName == sceneName) {
          same = true;
        }
      }
    })
  });

  if (!same) {
    throw (`bundle文件夹名称(${folderName})与scene名称不相同`);
  }

  return projectName;
}

function changeSettingToWebDesktop() {
  const path = 'assets'
  const folderName = getFolderName(path);
  editFolderMeta(path, folderName, false);
}

function changeSettingsToBundle() {
  const path = 'assets'
  const folderName = getFolderName(path);
  editFolderMeta(path, folderName, true);
}


async function replaceUuids() {
  console.log('build_step_0 开始~！');

  function editFolderMeta(path, folderName) {
    const metaPath = `${path}/${folderName}.meta`;
    const metaDataStr = fs.readFileSync(metaPath);
    const metaData = JSON.parse(metaDataStr);
    metaData.isBundle = false;
    fs.writeFileSync(metaPath, JSON.stringify(metaData));
  }
  function fileReplace(path, replaceStr, newStr) {
    if (!fs.existsSync(path)) {
      return;
    }
    const fileStr = fs.readFileSync(path);
    const newFileStr = fileStr.toString().replace(new RegExp(replaceStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), 'g'), newStr);
    fs.writeFileSync(path, newFileStr);
  }
  function getShortUuid(uuid) {
    const bytes = parse(uuid).subarray(1);
    return uuid.substring(0, 5) + Base64.fromUint8Array(bytes).substring(2);
  }

  const path = 'assets'
  function getFolderName(path) {
    let folderName = '';
    fs.readdirSync(path).find(fileName => {
      const st = fs.statSync(`${path}/${fileName}`);
      if (st.isDirectory()) {
        folderName = fileName;
      }
    });
    return folderName;
  }

  async function fileForEach(src, func) {
    //读取目录
    const st = fs.statSync(src);
    if (st.isFile()) {
      await func(src);
      return;
    }
    const paths = fs.readdirSync(src);
    for (let i = 0; i < paths.length; i++) {
      let path = paths[i];
      const newSrc = `${src}/${path}`;
      const st = fs.statSync(newSrc);
      if (st.isFile()) {
        await func(newSrc);
      } else if (st.isDirectory()) {
        await fileForEach(newSrc, func);
      }
    }
  }

  function createUuidData(uuid) {
    const shortUuid = getShortUuid(uuid);
    const newUuid = v4();
    const newShortUuid = getShortUuid(newUuid);
    return {
      oldUuid: uuid,
      oldShortUuid: shortUuid,
      newUuid: newUuid,
      newShortUuid: newShortUuid,
    };
  }

  const uuidList = [];
  await fileForEach(path, (path) => {
    const nameList = path.split('.');
    if (nameList[nameList.length - 1] == 'meta') {
      const data = JSON.parse(fs.readFileSync(path));
      uuidList.push(createUuidData(data.uuid));
      if (data.subMetas) {
        for (const key in data.subMetas) {
          const subMet = data.subMetas[key];
          if (subMet.uuid) {
            uuidList.push(createUuidData(subMet.uuid));
          }
        }
      }
    }
  });
  const pathList = [path, 'settings', 'project.json'];

  for (let i = 0; i < pathList.length; i++) {
    const path = pathList[i];
    await fileForEach(path, (path) => {
      const nameList = path.split('.');
      const expectNameList = ['png', 'jpg', 'mp3', 'wav'];
      if (expectNameList.includes(nameList[nameList.length - 1])) {
        return;
      }
      const data = fs.readFileSync(path);
      uuidList.forEach(uuiddata => {
        if (data.includes(uuiddata.oldUuid)) {
          fileReplace(path, uuiddata.oldUuid, uuiddata.newUuid);
        }
        if (data.includes(uuiddata.oldShortUuid)) {
          fileReplace(path, uuiddata.oldShortUuid, uuiddata.newShortUuid);
        }
      });
    });
  }

  console.log('build_step_0 完成~！');



  const sceneName = getFolderName(path);
  let scriptUUids = [];
  await fileForEach(path, (path) => {
    const nameList = path.split('.');
    if (nameList[nameList.length - 1] == 'meta') {
      if (['js', 'ts'].includes(nameList[nameList.length - 2])) {
        const scriptNameList = nameList[nameList.length - 3].split('/');
        const scriptName = scriptNameList[scriptNameList.length - 1];
        const data = JSON.parse(fs.readFileSync(path));
        scriptUUids.push({
          uuid: data.uuid,
          shortUuid: getShortUuid(data.uuid),
          scriptName: scriptName,
          fileName: nameList[nameList.length - 3] + '.' + nameList[nameList.length - 2],
        });
      }
    }
  });
  await fileForEach(path, (path) => {
    const nameList = path.split('.');
    const expectNameList = ['fire', 'prefab'];
    if (expectNameList.includes(nameList[nameList.length - 1])) {
      const data = fs.readFileSync(path);
      scriptUUids.forEach(uuiddata => {
        if (uuiddata.scriptName.includes(sceneName)) {
          uuiddata.includes = false;
          return;
        }
        if (uuiddata.includes == true) {
          return;
        }
        if (data.includes(uuiddata.shortUuid)) {
          uuiddata.includes = true;
        }
      });
    }
  });
  scriptUUids = scriptUUids.filter(uuiddata => uuiddata.includes);

  await fileForEach(path, (path) => {
    const nameList = path.split('.');
    const expectNameList = ['js', 'ts'];
    if (expectNameList.includes(nameList[nameList.length - 1])) {
      const data = fs.readFileSync(path);
      scriptUUids.forEach(uuiddata => {
        const strList = [
          `.getComponent("${uuiddata.scriptName}")`,
          `.getComponent('${uuiddata.scriptName}')`,
          `.getComponent(\`${uuiddata.scriptName}\`)`
        ];
        strList.forEach(str => {
          if (data.includes(str)) {
            fileReplace(path, str, `.getComponent("${sceneName}_${uuiddata.scriptName}")`);
          }
        });
      });
    }
  });
  scriptUUids.forEach((uuiddata) => {
    const data = fs.readFileSync(uuiddata.fileName);
    const metaData = fs.readFileSync(uuiddata.fileName + '.meta');
    const new_fileName = `${sceneName}_${uuiddata.scriptName}`;
    const newPath = uuiddata.fileName.split('/').map((str, idx, arr) => {
      if (idx == arr.length - 1) {
        const nameList = str.split('.');
        return `${new_fileName}.${nameList[nameList.length - 1]}`;
      }
      return str;
    }).join('/');
    fs.writeFileSync(newPath, data);
    fs.rmSync(uuiddata.fileName);
    fs.writeFileSync(newPath + '.meta', metaData);
    fs.rmSync(uuiddata.fileName + '.meta');
  });
  console.log(scriptUUids.map(uuiddata => uuiddata.scriptName));
}

function replaceIndexHtml() {
  const data = fs.readFileSync('index.html');
  fs.writeFileSync('dist/play/index.html', data);
}

module.exports = {

  replaceUuids: replaceUuids,

  build: async function () {

    const startTime = new Date().getTime();

    // 构建前检查
    const projectName = build_check();

    // 清理旧文件

    // 构建form
    await removeDir('dist/form');
    await buildForm();

    // 替换uuid
    await replaceUuids();

    // 改设置为非bundle
    changeSettingToWebDesktop();

    // 构建play
    await removeDir('dist/play');
    await buildWebDesktop();
    await copyDir('build/web-desktop', 'dist/play');
    replaceIndexHtml();
    console.log('构建 web desktop 成功！');

    // 改设置为bundle
    changeSettingsToBundle();

    await removeDir('dist/android');
    await buildAndroidBundle();
    await copyDir('build_android/jsb-link/remote', 'dist/android');
    console.log('构建 android bundle 成功！');

    await removeDir('dist/ios');
    await buildIosBundle();
    await copyDir('build_ios/jsb-link/remote', 'dist/ios');
    console.log('构建 ios bundle 成功！');

    await removeDir('dist/web_desktop');
    await buildWebBundle();
    await copyDir(`build_web_desktop/web-desktop/assets/${projectName}`, 'dist/web_desktop');
    console.log('构建 web bundle 成功！');

    // 改设置为非bundle
    changeSettingToWebDesktop();

    createConfigFile(projectName);

    compressAll(projectName);

    await removeDir('build');
    await removeDir('build_android');
    await removeDir('build_ios');
    await removeDir('build_web_desktop');

    const endTime = new Date().getTime();
    const duration = new Date(endTime - startTime);
    console.log(`打包完成！`);
    console.log(`用时${duration.getMinutes()}分${duration.getSeconds()}秒。`);
  },

  buildAndroid: async function () {

    // 构建前检查
    const projectName = build_check();
    // 替换uuid
    await replaceUuids();
    // 改设置为bundle
    changeSettingsToBundle();

    await removeDir('dist/android');
    await buildAndroidBundle();
    await copyDir('build_android/jsb-link/remote', 'dist/android');

    // 改设置为非bundle
    changeSettingToWebDesktop();
    createConfigFile(projectName, "android");
    await removeDir('build_android');
    console.log('构建 android bundle 成功！');
  },

  buildIos: async function () {

    // 构建前检查
    const projectName = build_check();
    // 替换uuid
    await replaceUuids();
    // 改设置为bundle
    changeSettingsToBundle();

    await removeDir('dist/ios');
    await buildIosBundle();
    await copyDir('build_ios/jsb-link/remote', 'dist/ios');

    // 改设置为非bundle
    changeSettingToWebDesktop();
    createConfigFile(projectName, "ios");
    await removeDir('build_ios');
    console.log('构建 ios bundle 成功！');
  }

};
