(function () {
  var ns = $.namespace('pskl.service');

  ns.CurrentColorsService = function (piskelController) {
    this.piskelController = piskelController;
    // cache of current colors by history state
    this.cache = {};
    this.currentColors = [];

    this.cachedFrameProcessor = new pskl.model.frame.AsyncCachedFrameProcessor();
    this.cachedFrameProcessor.setFrameProcessor(this.getFrameColors_.bind(this));

    this.throttledUpdateCurrentColors_ = pskl.utils.FunctionUtils.throttle(
      this.updateCurrentColors_.bind(this),
      1000
    );

    this.paletteService = pskl.app.paletteService;
  };

  ns.CurrentColorsService.prototype.init = function () {
    $.subscribe(Events.HISTORY_STATE_SAVED, this.throttledUpdateCurrentColors_);
    $.subscribe(Events.HISTORY_STATE_LOADED, this.loadColorsFromCache_.bind(this));
  };

  ns.CurrentColorsService.prototype.getCurrentColors = function () {
    return this.currentColors;
  };

  ns.CurrentColorsService.prototype.setCurrentColors = function (colors) {
    var historyIndex = pskl.app.historyService.currentIndex;
    this.cache[historyIndex] = colors;
    if (colors.join('') !== this.currentColors.join('')) {
      this.currentColors = colors;
      $.publish(Events.CURRENT_COLORS_UPDATED);
    }
  };

  ns.CurrentColorsService.prototype.isCurrentColorsPaletteSelected_ = function () {
    var paletteId = pskl.UserSettings.get(pskl.UserSettings.SELECTED_PALETTE);
    var palette = this.paletteService.getPaletteById(paletteId);

    return palette.id === Constants.CURRENT_COLORS_PALETTE_ID;
  };

  ns.CurrentColorsService.prototype.loadColorsFromCache_ = function () {
    var historyIndex = pskl.app.historyService.currentIndex;
    var colors = this.cache[historyIndex];
    if (colors) {
      this.setCurrentColors(colors);
    } else {
      this.updateCurrentColors_();
    }
  };

  var batchAll = function (frames, job) {
    var batches = [];
    frames = frames.slice(0);
    while (frames.length) {
      batches.push(frames.splice(0, 10));
    }
    var result = Q([]);
    batches.forEach(function (batch) {
      result = result.then(function (results) {
        return Q.all(batch.map(job)).then(function (partials) {
          return results.concat(partials);
        });
      });
    });
    return result;
  };

  var frameCache = {};
  ns.CurrentColorsService.prototype.updateCurrentColors_ = function () {
    var layers = this.piskelController.getLayers();
    var frames = layers.map(function (l) {return l.getFrames();}).reduce(function (p, n) {return p.concat(n);});
    var job = function (frame) {
      return this.cachedFrameProcessor.get(frame);
    }.bind(this);

    var colors = {};
    var framesToBatch = [];

    var removeColorsIfNotInCurrent = function(hash, color) {
      if (!frameCache[hash][color]) {
        delete colors[color];
      }
    };

    for (var i = 0, length = frames.length; i < length; ++i) {
      var frame = frames[i];
      var hash = frame.getHash();

      if (frameCache[hash]) {
        colors = Object.assign(colors, frameCache[hash]);

        var hashParts = hash.split('-');
        var hashVersion = parseInt(hashParts.pop());

        if (hashVersion > 0) {
          var lastColors = frameCache[hashParts.join('-') + '-' + (hashVersion - 1)];

          if (lastColors) {
            Object.keys(lastColors).forEach(removeColorsIfNotInCurrent.bind(this, hash));
          }
        }
      } else {
        framesToBatch.push(frame);
      }
    }

    var batchAllThen = function (colors, results) {
      results.forEach(function (result) {
        Object.keys(result).forEach(function (color) {
          colors[color] = true;
        });
      });

      // Remove transparent color from used colors
      delete colors[pskl.utils.colorToInt(Constants.TRANSPARENT_COLOR)];

      var hexColors = Object.keys(colors).map(function (color) {
        return pskl.utils.intToHex(color);
      });
      this.setCurrentColors(hexColors);
    }.bind(this, colors);

    if (framesToBatch.length === 0) {
      batchAllThen([colors]);
    } else {
      batchAll(framesToBatch, job).then(batchAllThen);
    }
  };

  ns.CurrentColorsService.prototype.isCurrentColorsPaletteSelected_ = function () {
    var paletteId = pskl.UserSettings.get(pskl.UserSettings.SELECTED_PALETTE);
    var palette = this.paletteService.getPaletteById(paletteId);

    return palette && palette.id === Constants.CURRENT_COLORS_PALETTE_ID;
  };

  ns.CurrentColorsService.prototype.loadColorsFromCache_ = function () {
    var historyIndex = pskl.app.historyService.currentIndex;
    var colors = this.cache[historyIndex];
    if (colors) {
      this.setCurrentColors(colors);
    }
  };

  ns.CurrentColorsService.prototype.getFrameColors_ = function (frame, processorCallback) {
    var frameColorsWorker = new pskl.worker.framecolors.FrameColors(frame,
      function (event) {
        frameCache[frame.getHash()] = event.data.colors;
        processorCallback(event.data.colors);
      },
      function () {},
      function (event) {processorCallback({});}
    );

    frameColorsWorker.process();
  };
})();

@@ -2313,31 +2313,8 @@
 %7D;%0A%0A
-  var frameCache = %7B%7D;%0A
   ns
@@ -2433,16 +2433,66 @@
 ayers();
+%0A%0A    // Concatenate all frames in a single array.
 %0A    var
@@ -2526,16 +2526,23 @@
 on (l) %7B
+%0A      
 return l
@@ -2554,16 +2554,21 @@
 rames();
+%0A    
 %7D).reduc
@@ -2586,16 +2586,23 @@
 (p, n) %7B
+%0A      
 return p
@@ -2616,25 +2616,38 @@
 (n);
+%0A    
 %7D);%0A
+%0A
     
-var job =
+batchAll(frames,
  fun
@@ -2733,891 +2733,63 @@
 his)
-;%0A
+)
 %0A    
-var colors = %7B%7D;%0A    var framesToBatch = %5B%5D;%0A%0A    var removeColorsIfNotInCurrent = function(hash, color) %7B%0A      if (!frameCache%5Bhash%5D%5Bcolor%5D) %7B%0A        delete colors%5Bcolor%5D;%0A      %7D%0A    %7D;%0A%0A    for (var i = 0, length = frames.length; i %3C length; ++i) %7B%0A      var frame = frames%5Bi%5D;%0A      var hash = frame.getHash();%0A%0A      if (frameCache%5Bhash%5D) %7B%0A        colors = Object.assign(colors, frameCache%5Bhash%5D);%0A%0A        var hashParts = hash.split('-');%0A        var hashVersion = parseInt(hashParts.pop());%0A%0A        if (hashVersion %3E 0) %7B%0A          var lastColors = frameCache%5BhashParts.join('-') + '-' + (hashVersion - 1)%5D;%0A%0A          if (lastColors) %7B%0A            Object.keys(lastColors).forEach(removeColorsIfNotInCurrent.bind(this, hash));%0A          %7D%0A        %7D%0A      %7D else %7B%0A        framesToBatch.push(frame);%0A      %7D%0A    %7D%0A%0A    var batchAllThen = function (colors, results) %7B
+.then(function (results) %7B%0A      var colors = %7B%7D;
 %0A   
@@ -2932,25 +2932,24 @@
 ;%0A      %7D);%0A
-%0A
       // Rem
@@ -3238,161 +3238,11 @@
 this
-, colors);%0A%0A    if (framesToBatch.length === 0) %7B%0A      batchAllThen(%5Bcolors%5D);%0A    %7D else %7B%0A      batchAll(framesToBatch, job).then(batchAllThen);%0A    %7D
+));
 %0A  %7D
@@ -3993,65 +3993,8 @@
 ) %7B%0A
-        frameCache%5Bframe.getHash()%5D = event.data.colors;%0A
     

remove extra caching from CurrentColorsService