function EventEmitter() {
  this._events = this._events || {};
  this._maxListeners = this._maxListeners || undefined;
}

EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;
EventEmitter.defaultMaxListeners = 10;
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!isNumber(n) || n < 0 || isNaN(n))
    throw TypeError("n must be a positive number");
  this._maxListeners = n;
  return this;
};
EventEmitter.prototype.emit = function(type) {
  var er, handler, len, args, i, listeners;
  if (!this._events) this._events = {};
  if (type === "error") {
    if (
      !this._events.error ||
      (isObject(this._events.error) && !this._events.error.length)
    ) {
      er = arguments[1];
      if (er instanceof Error) {
        throw er;
      } else {
        var err = new Error(
          'Uncaught, unspecified "error" event. (' + er + ")"
        );
        err.context = er;
        throw err;
      }
    }
  }
  handler = this._events[type];
  if (isUndefined(handler)) return false;
  if (isFunction(handler)) {
    switch (arguments.length) {
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      default:
        args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
  } else if (isObject(handler)) {
    args = Array.prototype.slice.call(arguments, 1);
    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++) listeners[i].apply(this, args);
  }
  return true;
};
EventEmitter.prototype.addListener = function(type, listener) {
  var m;
  if (!isFunction(listener)) throw TypeError("listener must be a function");
  if (!this._events) this._events = {};
  if (this._events.newListener)
    this.emit(
      "newListener",
      type,
      isFunction(listener.listener) ? listener.listener : listener
    );
  if (!this._events[type]) this._events[type] = listener;
  else if (isObject(this._events[type])) this._events[type].push(listener);
  else this._events[type] = [this._events[type], listener];
  if (isObject(this._events[type]) && !this._events[type].warned) {
    if (!isUndefined(this._maxListeners)) {
      m = this._maxListeners;
    } else {
      m = EventEmitter.defaultMaxListeners;
    }
    if (m && m > 0 && this._events[type].length > m) {
      this._events[type].warned = true;
      console.error(
        "(node) warning: possible EventEmitter memory " +
          "leak detected. %d listeners added. " +
          "Use emitter.setMaxListeners() to increase limit.",
        this._events[type].length
      );
      if (typeof console.trace === "function") {
        console.trace();
      }
    }
  }
  return this;
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.once = function(type, listener) {
  if (!isFunction(listener)) throw TypeError("listener must be a function");
  var fired = false;
  function g() {
    this.removeListener(type, g);
    if (!fired) {
      fired = true;
      listener.apply(this, arguments);
    }
  }
  g.listener = listener;
  this.on(type, g);
  return this;
};
EventEmitter.prototype.removeListener = function(type, listener) {
  var list, position, length, i;
  if (!isFunction(listener)) throw TypeError("listener must be a function");
  if (!this._events || !this._events[type]) return this;
  list = this._events[type];
  length = list.length;
  position = -1;
  if (
    list === listener ||
    (isFunction(list.listener) && list.listener === listener)
  ) {
    delete this._events[type];
    if (this._events.removeListener)
      this.emit("removeListener", type, listener);
  } else if (isObject(list)) {
    for (i = length; i-- > 0; ) {
      if (
        list[i] === listener ||
        (list[i].listener && list[i].listener === listener)
      ) {
        position = i;
        break;
      }
    }
    if (position < 0) return this;
    if (list.length === 1) {
      list.length = 0;
      delete this._events[type];
    } else {
      list.splice(position, 1);
    }
    if (this._events.removeListener)
      this.emit("removeListener", type, listener);
  }
  return this;
};
EventEmitter.prototype.removeAllListeners = function(type) {
  var key, listeners;
  if (!this._events) return this;
  if (!this._events.removeListener) {
    if (arguments.length === 0) this._events = {};
    else if (this._events[type]) delete this._events[type];
    return this;
  }
  if (arguments.length === 0) {
    for (key in this._events) {
      if (key === "removeListener") continue;
      this.removeAllListeners(key);
    }
    this.removeAllListeners("removeListener");
    this._events = {};
    return this;
  }
  listeners = this._events[type];
  if (isFunction(listeners)) {
    this.removeListener(type, listeners);
  } else if (listeners) {
    while (listeners.length)
      this.removeListener(type, listeners[listeners.length - 1]);
  }
  delete this._events[type];
  return this;
};
EventEmitter.prototype.listeners = function(type) {
  var ret;
  if (!this._events || !this._events[type]) ret = [];
  else if (isFunction(this._events[type])) ret = [this._events[type]];
  else ret = this._events[type].slice();
  return ret;
};
EventEmitter.prototype.listenerCount = function(type) {
  if (this._events) {
    var evlistener = this._events[type];
    if (isFunction(evlistener)) return 1;
    else if (evlistener) return evlistener.length;
  }
  return 0;
};
EventEmitter.listenerCount = function(emitter, type) {
  return emitter.listenerCount(type);
};
function isFunction(arg) {
  return typeof arg === "function";
}
function isNumber(arg) {
  return typeof arg === "number";
}
function isObject(arg) {
  return typeof arg === "object" && arg !== null;
}
function isUndefined(arg) {
  return arg === void 0;
}

var UA, browser, mode, platform, ua;
ua = navigator.userAgent.toLowerCase();
platform = navigator.platform.toLowerCase();
UA = ua.match(
  /(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/
) || [null, "unknown", 0];
mode = UA[1] === "ie" && document.documentMode;
browser = {
  name: UA[1] === "version" ? UA[3] : UA[1],
  version: mode || parseFloat(UA[1] === "opera" && UA[4] ? UA[4] : UA[2]),
  platform: {
    name: ua.match(/ip(?:ad|od|hone)/)
      ? "ios"
      : (ua.match(/(?:webos|android)/) ||
          platform.match(/mac|win|linux/) || ["other"])[0]
  }
};
browser[browser.name] = true;
browser[browser.name + parseInt(browser.version, 10)] = true;
browser.platform[browser.platform.name] = true;

var extend = function(child, parent) {
    for (var key in parent) {
      if (hasProp.call(parent, key)) child[key] = parent[key];
    }
    function ctor() {
      this.constructor = child;
    }
    ctor.prototype = parent.prototype;
    child.prototype = new ctor();
    child.__super__ = parent.prototype;
    return child;
  },
  hasProp = {}.hasOwnProperty,
  indexOf =
    [].indexOf ||
    function(item) {
      for (var i = 0, l = this.length; i < l; i++) {
        if (i in this && this[i] === item) return i;
      }
      return -1;
    },
  slice = [].slice;
var GIF = (function(superClass) {
  var defaults, frameDefaults;
  extend(GIF, superClass);
  var gifWorker = require("./gif.worker.js");

  defaults = {
    workerScript: gifWorker,
    workers: 2,
    repeat: 0,
    background: "#fff",
    quality: 10,
    width: null,
    height: null,
    transparent: null,
    debug: false,
    dither: false
  };

  frameDefaults = { delay: 500, copy: false };
  function GIF(options) {
    var base, key, value;
    this.running = false;
    this.options = {};
    this.frames = [];
    this.freeWorkers = [];
    this.activeWorkers = [];
    this.setOptions(options);
    for (key in defaults) {
      value = defaults[key];
      if ((base = this.options)[key] == null) {
        base[key] = value;
      }
    }
  }
  GIF.prototype.setOption = function(key, value) {
    this.options[key] = value;
    if (this._canvas != null && (key === "width" || key === "height")) {
      return (this._canvas[key] = value);
    }
  };
  GIF.prototype.setOptions = function(options) {
    var key, results, value;
    results = [];
    for (key in options) {
      if (!hasProp.call(options, key)) continue;
      value = options[key];
      results.push(this.setOption(key, value));
    }
    return results;
  };
  GIF.prototype.addFrame = function(image, options) {
    var frame, key;
    if (options == null) {
      options = {};
    }
    frame = {};
    frame.transparent = this.options.transparent;
    for (key in frameDefaults) {
      frame[key] = options[key] || frameDefaults[key];
    }
    if (this.options.width == null) {
      this.setOption("width", image.width);
    }
    if (this.options.height == null) {
      this.setOption("height", image.height);
    }
    if (
      typeof ImageData !== "undefined" &&
      ImageData !== null &&
      image instanceof ImageData
    ) {
      frame.data = image.data;
    } else if (
      (typeof CanvasRenderingContext2D !== "undefined" &&
        CanvasRenderingContext2D !== null &&
        image instanceof CanvasRenderingContext2D) ||
      (typeof WebGLRenderingContext !== "undefined" &&
        WebGLRenderingContext !== null &&
        image instanceof WebGLRenderingContext)
    ) {
      if (options.copy) {
        frame.data = this.getContextData(image);
      } else {
        frame.context = image;
      }
    } else if (image.childNodes != null) {
      if (options.copy) {
        frame.data = this.getImageData(image);
      } else {
        frame.image = image;
      }
    } else {
      throw new Error("Invalid image");
    }
    return this.frames.push(frame);
  };
  GIF.prototype.render = function() {
    var i, j, numWorkers, ref;
    if (this.running) {
      throw new Error("Already running");
    }
    if (this.options.width == null || this.options.height == null) {
      throw new Error("Width and height must be set prior to rendering");
    }
    this.running = true;
    this.nextFrame = 0;
    this.finishedFrames = 0;
    this.imageParts = function() {
      var j, ref, results;
      results = [];
      for (
        i = j = 0, ref = this.frames.length;
        0 <= ref ? j < ref : j > ref;
        i = 0 <= ref ? ++j : --j
      ) {
        results.push(null);
      }
      return results;
    }.call(this);
    numWorkers = this.spawnWorkers();
    if (this.options.globalPalette === true) {
      this.renderNextFrame();
    } else {
      for (
        i = j = 0, ref = numWorkers;
        0 <= ref ? j < ref : j > ref;
        i = 0 <= ref ? ++j : --j
      ) {
        this.renderNextFrame();
      }
    }
    this.emit("start");
    return this.emit("progress", 0);
  };
  GIF.prototype.abort = function() {
    var worker;
    while (true) {
      worker = this.activeWorkers.shift();
      if (worker == null) {
        break;
      }
      this.log("killing active worker");
      worker.terminate();
    }
    this.running = false;
    return this.emit("abort");
  };
  GIF.prototype.spawnWorkers = function() {
    var j, numWorkers, ref, results;
    numWorkers = Math.min(this.options.workers, this.frames.length);
    (function() {
      results = [];
      for (
        var j = (ref = this.freeWorkers.length);
        ref <= numWorkers ? j < numWorkers : j > numWorkers;
        ref <= numWorkers ? j++ : j--
      ) {
        results.push(j);
      }
      return results;
    }
      .apply(this)
      .forEach(
        (function(_this) {
          return function(i) {
            var worker;
            _this.log("spawning worker " + i);
            const Worker=require('./gif.worker')

            worker = new Worker.default();
            // worker = new Worker(_this.options.workerScript);
            worker.onmessage = function(event) {
              _this.activeWorkers.splice(
                _this.activeWorkers.indexOf(worker),
                1
              );
              _this.freeWorkers.push(worker);
              return _this.frameFinished(event.data);
            };
            return _this.freeWorkers.push(worker);
          };
        })(this)
      ));
    return numWorkers;
  };
  GIF.prototype.frameFinished = function(frame) {
    var i, j, ref;
    this.log(
      "frame " +
        frame.index +
        " finished - " +
        this.activeWorkers.length +
        " active"
    );
    this.finishedFrames++;
    this.emit("progress", this.finishedFrames / this.frames.length);
    this.imageParts[frame.index] = frame;
    if (this.options.globalPalette === true) {
      this.options.globalPalette = frame.globalPalette;
      this.log("global palette analyzed");
      if (this.frames.length > 2) {
        for (
          i = j = 1, ref = this.freeWorkers.length;
          1 <= ref ? j < ref : j > ref;
          i = 1 <= ref ? ++j : --j
        ) {
          this.renderNextFrame();
        }
      }
    }
    if (indexOf.call(this.imageParts, null) >= 0) {
      return this.renderNextFrame();
    } else {
      return this.finishRendering();
    }
  };
  GIF.prototype.finishRendering = function() {
    var data,
      frame,
      i,
      image,
      j,
      k,
      l,
      len,
      len1,
      len2,
      len3,
      offset,
      page,
      ref,
      ref1,
      ref2;
    len = 0;
    ref = this.imageParts;
    for (j = 0, len1 = ref.length; j < len1; j++) {
      frame = ref[j];
      len += (frame.data.length - 1) * frame.pageSize + frame.cursor;
    }
    len += frame.pageSize - frame.cursor;
    this.log("rendering finished - filesize " + Math.round(len / 1e3) + "kb");
    data = new Uint8Array(len);
    offset = 0;
    ref1 = this.imageParts;
    for (k = 0, len2 = ref1.length; k < len2; k++) {
      frame = ref1[k];
      ref2 = frame.data;
      for (i = l = 0, len3 = ref2.length; l < len3; i = ++l) {
        page = ref2[i];
        data.set(page, offset);
        if (i === frame.data.length - 1) {
          offset += frame.cursor;
        } else {
          offset += frame.pageSize;
        }
      }
    }
    image = new Blob([data], { type: "image/gif" });
    return this.emit("finished", image, data);
  };
  GIF.prototype.renderNextFrame = function() {
    var frame, task, worker;
    if (this.freeWorkers.length === 0) {
      throw new Error("No free workers");
    }
    if (this.nextFrame >= this.frames.length) {
      return;
    }
    frame = this.frames[this.nextFrame++];
    worker = this.freeWorkers.shift();
    task = this.getTask(frame);
    this.log(
      "starting frame " + (task.index + 1) + " of " + this.frames.length
    );
    this.activeWorkers.push(worker);
    return worker.postMessage(task);
  };
  GIF.prototype.getContextData = function(ctx) {
    return ctx.getImageData(0, 0, this.options.width, this.options.height).data;
  };
  GIF.prototype.getImageData = function(image) {
    var ctx;
    if (this._canvas == null) {
      this._canvas = document.createElement("canvas");
      this._canvas.width = this.options.width;
      this._canvas.height = this.options.height;
    }
    ctx = this._canvas.getContext("2d");
    ctx.setFill = this.options.background;
    ctx.fillRect(0, 0, this.options.width, this.options.height);
    ctx.drawImage(image, 0, 0);
    return this.getContextData(ctx);
  };
  GIF.prototype.getTask = function(frame) {
    var index, task;
    index = this.frames.indexOf(frame);
    task = {
      index: index,
      last: index === this.frames.length - 1,
      delay: frame.delay,
      transparent: frame.transparent,
      width: this.options.width,
      height: this.options.height,
      quality: this.options.quality,
      dither: this.options.dither,
      globalPalette: this.options.globalPalette,
      repeat: this.options.repeat,
      canTransfer: browser.name === "chrome"
    };
    if (frame.data != null) {
      task.data = frame.data;
    } else if (frame.context != null) {
      task.data = this.getContextData(frame.context);
    } else if (frame.image != null) {
      task.data = this.getImageData(frame.image);
    } else {
      throw new Error("Invalid frame");
    }
    return task;
  };
  GIF.prototype.log = function() {
    var args;
    args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
    if (!this.options.debug) {
      return;
    }
    return console.log.apply(console, args);
  };
  return GIF;
})(EventEmitter);

export default GIF;
