/*
Copyright (c) Daybrush
name: @scenejs/render
license: MIT
author: Daybrush
repository: git+https://github.com/daybrush/scenejs-render.git
version: 0.10.3
*/
import puppeteer from 'puppeteer';
import { existsSync, readdirSync, lstatSync, unlinkSync, rmdirSync, readFileSync, mkdirSync, writeFileSync, copyFileSync } from 'fs';
import { isUndefined } from '@daybrush/utils';
import { fork } from 'child_process';
import ffmpeg from 'fluent-ffmpeg';
import { resolve } from 'path';
import { pathToFileURL } from 'url';
import EventEmitter from '@scena/event-emitter';

/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0

THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.

See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */

/* global Reflect, Promise */
var extendStatics = function (d, b) {
  extendStatics = Object.setPrototypeOf || {
    __proto__: []
  } instanceof Array && function (d, b) {
    d.__proto__ = b;
  } || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  };

  return extendStatics(d, b);
};

function __extends(d, b) {
  extendStatics(d, b);

  function __() {
    this.constructor = d;
  }

  d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
var __assign = function () {
  __assign = Object.assign || function __assign(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
      s = arguments[i];

      for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
    }

    return t;
  };

  return __assign.apply(this, arguments);
};
function __awaiter(thisArg, _arguments, P, generator) {
  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }

    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }

    function step(result) {
      result.done ? resolve(result.value) : new P(function (resolve) {
        resolve(result.value);
      }).then(fulfilled, rejected);
    }

    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
}
function __generator(thisArg, body) {
  var _ = {
    label: 0,
    sent: function () {
      if (t[0] & 1) throw t[1];
      return t[1];
    },
    trys: [],
    ops: []
  },
      f,
      y,
      t,
      g;
  return g = {
    next: verb(0),
    "throw": verb(1),
    "return": verb(2)
  }, typeof Symbol === "function" && (g[Symbol.iterator] = function () {
    return this;
  }), g;

  function verb(n) {
    return function (v) {
      return step([n, v]);
    };
  }

  function step(op) {
    if (f) throw new TypeError("Generator is already executing.");

    while (_) try {
      if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
      if (y = 0, t) op = [op[0] & 2, t.value];

      switch (op[0]) {
        case 0:
        case 1:
          t = op;
          break;

        case 4:
          _.label++;
          return {
            value: op[1],
            done: false
          };

        case 5:
          _.label++;
          y = op[1];
          op = [0];
          continue;

        case 7:
          op = _.ops.pop();

          _.trys.pop();

          continue;

        default:
          if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
            _ = 0;
            continue;
          }

          if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
            _.label = op[1];
            break;
          }

          if (op[0] === 6 && _.label < t[1]) {
            _.label = t[1];
            t = op;
            break;
          }

          if (t && _.label < t[2]) {
            _.label = t[2];

            _.ops.push(op);

            break;
          }

          if (t[2]) _.ops.pop();

          _.trys.pop();

          continue;
      }

      op = body.call(thisArg, _);
    } catch (e) {
      op = [6, e];
      y = 0;
    } finally {
      f = t = 0;
    }

    if (op[0] & 5) throw op[1];
    return {
      value: op[0] ? op[1] : void 0,
      done: true
    };
  }
}

function resolvePath(path1, path2) {
  var paths = path1.split("/").slice(0, -1).concat(path2.split("/"));
  paths = paths.filter(function (directory, i) {
    return i === 0 || directory !== ".";
  });
  var index = -1; // tslint:disable-next-line: no-conditional-assignment

  while ((index = paths.indexOf("..")) > 0) {
    paths.splice(index - 1, 2);
  }

  return paths.join("/");
}
function rmdir(path) {
  if (existsSync(path)) {
    readdirSync(path).forEach(function (file) {
      var currentPath = path + "/" + file;

      if (lstatSync(currentPath).isDirectory()) {
        // recurse
        rmdir(currentPath);
      } else {
        // delete file
        unlinkSync(currentPath);
      }
    });
    rmdirSync(path);
  }
}
function sendMessage(message) {
  process.send && process.send(message);
}
function caputreLoop(_a) {
  var isOnlyMedia = _a.isOnlyMedia,
      page = _a.page,
      name = _a.name,
      delay = _a.delay,
      fps = _a.fps,
      media = _a.media,
      isMedia = _a.isMedia,
      playSpeed = _a.playSpeed,
      startFrame = _a.startFrame,
      endFrame = _a.endFrame,
      endTime = _a.endTime,
      totalFrame = _a.totalFrame;
  return __awaiter(this, void 0, void 0, function () {
    function loop(frame) {
      return __awaiter(this, void 0, void 0, function () {
        var time, _a, _b;

        return __generator(this, function (_c) {
          switch (_c.label) {
            case 0:
              time = Math.min(frame * playSpeed / fps, endTime);
              console.log("Capture frame: " + frame + ", time: " + time);
              _a = !isOnlyMedia;
              if (!_a) return [3
              /*break*/
              , 2];
              return [4
              /*yield*/
              , page.evaluate(name + ".setTime(" + (time - delay) + ", true)")];

            case 1:
              _a = _c.sent();
              _c.label = 2;

            case 2:
              _b = isMedia;
              if (!_b) return [3
              /*break*/
              , 4];
              return [4
              /*yield*/
              , page.evaluate(media + ".setTime(" + time + ")")];

            case 3:
              _b = _c.sent();
              _c.label = 4;

            case 4:
              return [4
              /*yield*/
              , page.screenshot({
                path: "./.scene_cache/frame" + frame + ".png"
              })];

            case 5:
              _c.sent();

              sendMessage({
                type: "capture",
                frame: frame,
                totalFrame: totalFrame
              });

              if (time === endTime || frame >= endFrame) {
                return [2
                /*return*/
                ];
              }

              return [4
              /*yield*/
              , loop(frame + 1)];

            case 6:
              _c.sent();

              return [2
              /*return*/
              ];
          }
        });
      });
    }

    return __generator(this, function (_b) {
      switch (_b.label) {
        case 0:
          return [4
          /*yield*/
          , loop(startFrame)];

        case 1:
          _b.sent();

          return [2
          /*return*/
          ];
      }
    });
  });
}
function openPage(_a) {
  var browser = _a.browser,
      width = _a.width,
      height = _a.height,
      path = _a.path,
      scale = _a.scale,
      name = _a.name,
      media = _a.media,
      referer = _a.referer;
  return __awaiter(this, void 0, void 0, function () {
    var page, e_1, e_2;
    return __generator(this, function (_b) {
      switch (_b.label) {
        case 0:
          return [4
          /*yield*/
          , browser.newPage()];

        case 1:
          page = _b.sent();
          page.setUserAgent(browser.userAgent() + " Scene.js");
          page.setViewport({
            width: width / scale,
            height: height / scale,
            deviceScaleFactor: scale
          });
          return [4
          /*yield*/
          , page.goto(path, {
            referer: referer
          })];

        case 2:
          _b.sent();

          _b.label = 3;

        case 3:
          _b.trys.push([3, 5,, 6]);

          return [4
          /*yield*/
          , page.evaluate(name + ".finish()")];

        case 4:
          _b.sent();

          return [3
          /*break*/
          , 6];

        case 5:
          e_1 = _b.sent();
          return [3
          /*break*/
          , 6];

        case 6:
          _b.trys.push([6, 8,, 9]);

          return [4
          /*yield*/
          , page.evaluate(media + ".finish()")];

        case 7:
          _b.sent();

          return [3
          /*break*/
          , 9];

        case 8:
          e_2 = _b.sent();
          return [3
          /*break*/
          , 9];

        case 9:
          return [2
          /*return*/
          , page];
      }
    });
  });
}

function forkCapture(datas) {
  return __awaiter(this, void 0, void 0, function () {
    var compute;
    return __generator(this, function (_a) {
      compute = fork(__dirname + "/subcapture.js");
      return [2
      /*return*/
      , new Promise(function (resolve) {
        compute.on("message", function (result) {
          sendMessage(result);
        });
        compute.on("close", function () {
          resolve();
        });
        compute.on("exit", function () {
          resolve();
        });
        compute.send(datas);
      })];
    });
  });
}

function getMediaInfo(page, media) {
  return __awaiter(this, void 0, void 0, function () {
    var e_1;
    return __generator(this, function (_a) {
      switch (_a.label) {
        case 0:
          if (!media) {
            return [2
            /*return*/
            ];
          }

          _a.label = 1;

        case 1:
          _a.trys.push([1, 3,, 4]);

          return [4
          /*yield*/
          , page.evaluate(media + ".finish().getInfo()")];

        case 2:
          return [2
          /*return*/
          , _a.sent()];

        case 3:
          e_1 = _a.sent();
          return [3
          /*break*/
          , 4];

        case 4:
          return [2
          /*return*/
          ];
      }
    });
  });
}

function captureScene(_a) {
  var name = _a.name,
      media = _a.media,
      path = _a.path,
      _b = _a.startTime,
      startTime = _b === void 0 ? 0 : _b,
      duration = _a.duration,
      iteration = _a.iteration,
      fps = _a.fps,
      width = _a.width,
      height = _a.height,
      cache = _a.cache,
      scale = _a.scale,
      multi = _a.multi,
      isVideo = _a.isVideo,
      referer = _a.referer;
  return __awaiter(this, void 0, void 0, function () {
    var browser, page, mediaInfo, isMedia, isOnlyMedia, iterationCount, delay, playSpeed, sceneDuration, totalDuration, endTime, startFrame, endFrame, _c, e_2, isCache, cacheInfo, temp, dist, loops, i, processStartFrame, processEndFrame, mainLoop;

    return __generator(this, function (_d) {
      switch (_d.label) {
        case 0:
          return [4
          /*yield*/
          , puppeteer.launch({
            headless: true
          })];

        case 1:
          browser = _d.sent();
          return [4
          /*yield*/
          , openPage({
            browser: browser,
            name: name,
            media: media,
            width: width,
            height: height,
            path: path,
            scale: scale,
            referer: referer
          })];

        case 2:
          page = _d.sent();
          return [4
          /*yield*/
          , getMediaInfo(page, media)];

        case 3:
          mediaInfo = _d.sent();
          isMedia = !!mediaInfo;

          if (!isVideo) {
            console.log("No Video");
            return [2
            /*return*/
            , {
              mediaInfo: mediaInfo || {},
              duration: isMedia ? mediaInfo.duration : 0
            }];
          }

          isOnlyMedia = false;
          _d.label = 4;

        case 4:
          _d.trys.push([4, 10,, 11]);

          _c = iteration;
          if (_c) return [3
          /*break*/
          , 6];
          return [4
          /*yield*/
          , page.evaluate(name + ".getIterationCount()")];

        case 5:
          _c = _d.sent();
          _d.label = 6;

        case 6:
          iterationCount = _c;
          return [4
          /*yield*/
          , page.evaluate(name + ".getDelay()")];

        case 7:
          delay = _d.sent();
          return [4
          /*yield*/
          , page.evaluate(name + ".getPlaySpeed()")];

        case 8:
          playSpeed = _d.sent();
          return [4
          /*yield*/
          , page.evaluate(name + ".getDuration()")];

        case 9:
          sceneDuration = _d.sent();

          if (iterationCount === "infinite") {
            iterationCount = iteration || 1;
          }

          totalDuration = delay + sceneDuration * iterationCount;
          endTime = duration > 0 ? Math.min(startTime + duration, totalDuration) : totalDuration;
          startFrame = Math.floor(startTime * fps / playSpeed);
          endFrame = Math.ceil(endTime * fps / playSpeed);
          return [3
          /*break*/
          , 11];

        case 10:
          e_2 = _d.sent();

          if (isMedia) {
            console.log("Only Media Scene");
            isOnlyMedia = true;
            iterationCount = 1;
            delay = 0;
            playSpeed = 1;
            sceneDuration = mediaInfo.duration;
            endTime = isUndefined(duration) ? sceneDuration : Math.min(startTime + duration, sceneDuration);
            startFrame = Math.floor(startTime * fps / playSpeed);
            endFrame = Math.ceil(endTime * fps / playSpeed);
          } else {
            throw e_2;
          }

          return [3
          /*break*/
          , 11];

        case 11:
          isCache = false;

          if (cache) {
            try {
              cacheInfo = readFileSync("./.scene_cache/cache.txt", "utf8");
              temp = JSON.stringify({
                startTime: startTime,
                endTime: endTime,
                fps: fps,
                startFrame: startFrame,
                endFrame: endFrame
              });

              if (cacheInfo === temp) {
                isCache = true;
              }
            } catch (e) {
              isCache = false;
            }
          }

          !isCache && rmdir("./.scene_cache");
          !existsSync("./.scene_cache") && mkdirSync("./.scene_cache");
          sendMessage({
            type: "captureStart",
            isCache: isCache,
            duration: (endTime - startTime) / playSpeed
          });
          if (!isCache) return [3
          /*break*/
          , 12];
          console.log("Use Cache (startTime: " + startTime + ", endTime: " + endTime + ", fps: " + fps + ", startFrame: " + startFrame + ", endFrame: " + endFrame + ")");
          return [3
          /*break*/
          , 14];

        case 12:
          console.log("Start Capture (startTime: " + startTime + ", endTime: " + endTime + ", fps: " + fps + ", startFrame: " + startFrame + ", endFrame: " + endFrame + ", multi-process: " + multi + ")");
          dist = Math.ceil((endFrame - startFrame) / multi);
          loops = [];

          for (i = 1; i < multi; ++i) {
            processStartFrame = startFrame + dist * i + 1;
            processEndFrame = startFrame + dist * (i + 1);
            loops.push(forkCapture({
              isOnlyMedia: isOnlyMedia,
              name: name,
              media: media,
              path: path,
              endTime: endTime,
              fps: fps,
              width: width,
              height: height,
              scale: scale,
              delay: delay,
              playSpeed: playSpeed,
              startFrame: processStartFrame,
              endFrame: processEndFrame,
              isMedia: isMedia,
              totalFrame: endFrame,
              referer: referer
            }));
          }

          mainLoop = caputreLoop({
            isOnlyMedia: isOnlyMedia,
            page: page,
            name: name,
            fps: fps,
            delay: delay,
            media: media,
            isMedia: isMedia,
            playSpeed: playSpeed,
            startFrame: startFrame,
            endFrame: startFrame + dist,
            endTime: endTime,
            totalFrame: endFrame
          });
          loops.push(mainLoop);
          return [4
          /*yield*/
          , Promise.all(loops)];

        case 13:
          _d.sent();

          _d.label = 14;

        case 14:
          writeFileSync("./.scene_cache/cache.txt", JSON.stringify({
            startTime: startTime,
            endTime: endTime,
            fps: fps,
            startFrame: startFrame,
            endFrame: endFrame
          }));
          return [4
          /*yield*/
          , browser.close()];

        case 15:
          _d.sent();

          return [2
          /*return*/
          , {
            mediaInfo: mediaInfo || {},
            duration: (endTime - startTime) / playSpeed
          }];
      }
    });
  });
}

function convertAudio(_a) {
  var i = _a.i,
      path = _a.path,
      delay = _a.delay,
      seek = _a.seek,
      playSpeed = _a.playSpeed,
      volume = _a.volume;
  return __awaiter(this, void 0, void 0, function () {
    var startTime, endTime;
    return __generator(this, function (_b) {
      console.log("Convert Audio", path);
      startTime = seek[0], endTime = seek[1];
      return [2
      /*return*/
      , new Promise(function (resolve) {
        ffmpeg(path).seekInput(startTime).inputOptions("-to " + endTime).audioFilters(["adelay=" + delay * playSpeed * 1000 + "|" + delay * playSpeed * 1000, "atempo=" + playSpeed, "volume=" + volume]).on("error", function (err) {
          console.log("An audio error occurred: " + err.message);
          resolve();
        }).on("end", function () {
          resolve();
        }).save("./.scene_cache/audio" + i + ".mp3");
      })];
    });
  });
}

function processMedia(mediaInfo, input, output) {
  return __awaiter(this, void 0, void 0, function () {
    var length, medias, duration, result;
    return __generator(this, function (_a) {
      switch (_a.label) {
        case 0:
          console.log("Process Media");
          length = 0;
          medias = mediaInfo.medias;
          duration = mediaInfo.duration;

          if (!duration || !medias) {
            return [2
            /*return*/
            , false];
          }

          !existsSync("./.scene_cache") && mkdirSync("./.scene_cache");
          return [4
          /*yield*/
          , Promise.all(medias.map(function (media) {
            var url = media.url;
            var seek = media.seek;
            var delay = media.delay;
            var playSpeed = media.playSpeed;
            var volume = media.volume;
            var path = url.match(/https*:\/\//g) ? url : resolvePath(input, url);
            return convertAudio({
              i: length++,
              path: path,
              delay: delay,
              seek: seek,
              playSpeed: playSpeed,
              volume: volume
            });
          }))];

        case 1:
          _a.sent();

          if (!length) {
            return [2
            /*return*/
            , false];
          }

          console.log("Merge Medias");
          return [4
          /*yield*/
          , new Promise(function (resolve, reject) {
            var converter = ffmpeg();
            var inputLengths = 0;

            for (var i = 0; i < length; ++i) {
              if (existsSync("./.scene_cache/audio" + i + ".mp3")) {
                converter.addInput("./.scene_cache/audio" + i + ".mp3");
                ++inputLengths;
              }
            }

            converter.inputOptions("-filter_complex amix=inputs=" + inputLengths + ":duration=longest").on("error", function (err) {
              console.log("An merge error occurred: " + err.message);
              reject(false);
            }).on("end", function () {
              resolve(true);
            }).save("./.scene_cache/merge.mp3");
          })];

        case 2:
          result = _a.sent();

          if (result && output) {
            copyFileSync("./.scene_cache/merge.mp3", output);
          }

          return [2
          /*return*/
          , result];
      }
    });
  });
}

var DEFAULT_CODECS = {
  mp4: "libx264",
  webm: "libvpx-vp9"
};
var ERROR = "error";
var IDLE = "idle";
var START = "start";
var FINISH = "finish";
var CAPTURING = "capturing";
var PROCESSING = "processing";

function processVideo(_a) {
  var duration = _a.duration,
      codec = _a.codec,
      fps = _a.fps,
      output = _a.output,
      width = _a.width,
      height = _a.height,
      isMedia = _a.isMedia,
      bitrate = _a.bitrate,
      multi = _a.multi;
  return __awaiter(this, void 0, void 0, function () {
    var ext;

    var _this = this;

    return __generator(this, function (_b) {
      ext = output.match(/(?<=\.)[^.]+$/g);
      codec = codec || ext && DEFAULT_CODECS[ext[0]] || DEFAULT_CODECS.mp4;
      return [2
      /*return*/
      , new Promise(function (resolve, reject) {
        return __awaiter(_this, void 0, void 0, function () {
          var frames, totalFrame, i, converter;
          return __generator(this, function (_a) {
            frames = [];
            totalFrame = duration * fps;

            for (i = 0; i <= totalFrame; ++i) {
              frames[i] = "./.scene_cache/frame" + i + ".png";
            }

            console.log("Processing start (width: " + width + ", height: " + height + ", totalframe: " + (totalFrame + 1) + ", duration: " + duration + ", fps: " + fps + ", media: " + isMedia + ")");
            sendMessage({
              type: "process",
              processing: 0
            });
            converter = ffmpeg().addInput("./.scene_cache/frame%d.png") // .addInput('./test.mp3')
            .inputFPS(fps).loop(duration).on("error", function (err) {
              console.log("An error occurred: " + err.message);
              reject();
            }).on("progress", function (progress) {
              var percent = (progress.frames || 0) / (totalFrame + 1) * 100;
              sendMessage({
                type: "process",
                processing: percent
              });
              console.log("Processing: " + percent + "% done");
            }).on("end", function () {
              console.log("Processing finished !");
              resolve();
            }).videoBitrate(bitrate).videoCodec(codec).outputOption(["-cpu-used " + multi, "-pix_fmt yuv420p"]).size(width + "x" + height).format("mp4");

            if (isMedia) {
              converter.addInput("./.scene_cache/merge.mp3").audioCodec("aac").audioBitrate("128k") // .audioFrequency(22050)
              .audioChannels(2);
            }

            converter.save(output);
            return [2
            /*return*/
            ];
          });
        });
      })];
    });
  });
}

function render(_a) {
  var _b = _a === void 0 ? {} : _a,
      _c = _b.name,
      name = _c === void 0 ? "scene" : _c,
      _d = _b.media,
      media = _d === void 0 ? "mediaScene" : _d,
      _e = _b.fps,
      fps = _e === void 0 ? 60 : _e,
      _f = _b.width,
      width = _f === void 0 ? 1920 : _f,
      _g = _b.height,
      height = _g === void 0 ? 1080 : _g,
      _h = _b.output,
      output = _h === void 0 ? "output.mp4" : _h,
      startTime = _b.startTime,
      cache = _b.cache,
      scale = _b.scale,
      multi = _b.multi,
      _j = _b.input,
      input = _j === void 0 ? "./index.html" : _j,
      _k = _b.duration,
      duration = _k === void 0 ? 0 : _k,
      _l = _b.iteration,
      iteration = _l === void 0 ? 0 : _l,
      _m = _b.bitrate,
      bitrate = _m === void 0 ? "4096k" : _m,
      codec = _b.codec,
      referer = _b.referer,
      ffmpegPath = _b.ffmpegPath;

  return __awaiter(this, void 0, void 0, function () {
    var path, outputs, videoOutputs, isVideo, audioPath, startProcessingTime, _o, sceneDuration_1, mediaInfo, isMedia_1, endProcessingTime, e_1;

    return __generator(this, function (_p) {
      switch (_p.label) {
        case 0:
          if (input.match(/https*:\/\//g)) {
            path = input;
          } else {
            path = pathToFileURL(resolve(process.cwd(), input)).href;
          }

          console.log("Open Page: ", path);
          _p.label = 1;

        case 1:
          _p.trys.push([1, 6,, 7]);

          console.log("Start Rendering");
          outputs = output.split(",");
          videoOutputs = outputs.filter(function (file) {
            return file.match(/\.(mp4|webm)$/g);
          });
          isVideo = videoOutputs.length;
          audioPath = outputs.find(function (file) {
            return file.match(/\.mp3$/g);
          });
          startProcessingTime = Date.now();
          return [4
          /*yield*/
          , captureScene({
            media: media,
            name: name,
            path: path,
            fps: fps,
            width: width,
            height: height,
            startTime: startTime,
            duration: duration,
            iteration: iteration,
            cache: cache,
            scale: scale,
            multi: multi,
            isVideo: isVideo,
            referer: referer
          })];

        case 2:
          _o = _p.sent(), sceneDuration_1 = _o.duration, mediaInfo = _o.mediaInfo;

          if (ffmpegPath) {
            process.env.PATH = resolve(process.cwd(), ffmpegPath) + ":" + process.env.PATH;
          }

          return [4
          /*yield*/
          , processMedia(mediaInfo, input, audioPath)];

        case 3:
          isMedia_1 = _p.sent();
          if (!isVideo) return [3
          /*break*/
          , 5];
          return [4
          /*yield*/
          , Promise.all(videoOutputs.map(function (file) {
            return processVideo({
              duration: sceneDuration_1,
              bitrate: bitrate,
              codec: codec,
              fps: fps,
              output: file,
              width: width,
              height: height,
              multi: multi,
              isMedia: isMedia_1
            });
          }))];

        case 4:
          _p.sent();

          _p.label = 5;

        case 5:
          !cache && rmdir("./.scene_cache");
          endProcessingTime = Date.now();
          console.log("End Rendering(Rendering Time: " + (endProcessingTime - startProcessingTime) / 1000 + "s)");
          return [3
          /*break*/
          , 7];

        case 6:
          e_1 = _p.sent();
          console.error(e_1);
          process.exit(200);
          return [2
          /*return*/
          ];

        case 7:
          process.exit(0);
          return [2
          /*return*/
          ];
      }
    });
  });
}

var Renderer =
/*#__PURE__*/
function (_super) {
  __extends(Renderer, _super);

  function Renderer() {
    var _this = _super !== null && _super.apply(this, arguments) || this;

    _this.startTime = 0;
    _this.endTime = 0;
    _this.captureFrames = 0;
    _this.progress = 0;
    _this.status = IDLE;
    _this.process = null;
    _this.pathname = "";
    _this.isEnd = false;
    return _this;
  }

  var __proto = Renderer.prototype;

  __proto.start = function (options) {
    var _this = this;

    if (options === void 0) {
      options = {};
    }

    this.startTime = Date.now();
    this.endTime = 0;
    this.captureFrames = 0;
    this.progress = 0;
    this.status = START;
    this.isEnd = false;
    this.trigger("start", options);
    this.process = fork(resolve(__dirname, "../index.js"), Object.keys(options).map(function (key) {
      return "--" + key + "=" + options[key];
    }));
    this.process.on("message", function (message) {
      var type = message.type;

      if (type === "captureStart") {
        _this.status = CAPTURING;

        _this.trigger("captureStart", {
          isCache: message.isCache,
          duration: message.duration
        });
      } else if (type === "capture") {
        // capturing 90%
        _this.status = CAPTURING;
        ++_this.captureFrames;
        _this.progress = _this.captureFrames / message.totalFrame * 90;

        _this.trigger("render");

        _this.trigger("capture", {
          frame: message.frame,
          frameCount: _this.captureFrames,
          totalFrame: message.totalFrame
        });
      } else if (type === "process") {
        // processing 10%
        _this.status = PROCESSING;
        _this.progress = Math.min(100, 90 + message.processing / 100 * 10);

        if (_this.progress >= 100) {
          _this.isEnd = true;
        }

        _this.trigger("render");

        _this.trigger("process", {
          processing: message.processing
        });
      } else {
        // end
        _this.finish();
      }
    });
    this.process.on("error", function () {
      console.log("ERR");

      _this.cancel();
    });
    this.process.on("close", function () {
      console.log("CLOSE");

      _this.finish();
    });
    this.process.on("exit", function (exitCode) {
      console.log("EXIT", exitCode);

      if (exitCode === 200) {
        _this.cancel(true);
      } else {
        _this.finish();
      }
    });
  };

  __proto.finish = function () {
    if (!this.process) {
      return;
    }

    this.process = null;
    this.progress = 100;

    if (!this.isEnd) {
      this.trigger("render");
      this.trigger("process", {
        processing: 100
      });
    }

    this.status = FINISH;
    this.endTime = Date.now();
    this.trigger("finish");
  };

  __proto.cancel = function (isError) {
    if (!this.process) {
      return;
    }

    this.status = isError ? ERROR : IDLE;

    if (isError) {
      this.trigger("error");
    } else {
      this.trigger("cancel");
    }

    this.process.kill();
    this.process = null;
    this.startTime = 0;
    this.endTime = 0;
    this.progress = 0;
    this.captureFrames = 0;
  };

  __proto.getProgressInfo = function () {
    var time = 0;
    var status = this.status;

    if (status === FINISH) {
      time = this.endTime - this.startTime;
    } else if (status !== START && status !== IDLE) {
      time = Date.now() - this.startTime;
    }

    return {
      time: time,
      progress: this.progress,
      status: status
    };
  };

  __proto.trigger = function (name, param) {
    if (param === void 0) {
      param = {};
    }

    var info = this.getProgressInfo();
    return _super.prototype.trigger.call(this, name, __assign({}, info, param));
  };

  return Renderer;
}(EventEmitter);

export { Renderer, render };
//# sourceMappingURL=render.esm.js.map
