/**
 * ImageScanner.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2017 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * Finds images with data uris or blob uris. If data uris are found it will convert them into blob uris.
 *
 * @private
 * @class tinymce.file.ImageScanner
 */
define(
  'tinymce.core.file.ImageScanner',
  [
    "tinymce.core.util.Promise",
    "tinymce.core.util.Arr",
    "tinymce.core.util.Fun",
    "tinymce.core.file.Conversions",
    "tinymce.core.Env"
  ],
  function (Promise, Arr, Fun, Conversions, Env) {
    var count = 0;

    var uniqueId = function (prefix) {
      return (prefix || 'blobid') + (count++);
    };

    var imageToBlobInfo = function (blobCache, img, resolve, reject) {
      var base64, blobInfo;

      if (img.src.indexOf('blob:') === 0) {
        blobInfo = blobCache.getByUri(img.src);

        if (blobInfo) {
          resolve({
            image: img,
            blobInfo: blobInfo
          });
        } else {
          Conversions.uriToBlob(img.src).then(function (blob) {
            Conversions.blobToDataUri(blob).then(function (dataUri) {
              base64 = Conversions.parseDataUri(dataUri).data;
              blobInfo = blobCache.create(uniqueId(), blob, base64);
              blobCache.add(blobInfo);

              resolve({
                image: img,
                blobInfo: blobInfo
              });
            });
          }, function (err) {
            reject(err);
          });
        }

        return;
      }

      base64 = Conversions.parseDataUri(img.src).data;
      blobInfo = blobCache.findFirst(function (cachedBlobInfo) {
        return cachedBlobInfo.base64() === base64;
      });

      if (blobInfo) {
        resolve({
          image: img,
          blobInfo: blobInfo
        });
      } else {
        Conversions.uriToBlob(img.src).then(function (blob) {
          blobInfo = blobCache.create(uniqueId(), blob, base64);
          blobCache.add(blobInfo);

          resolve({
            image: img,
            blobInfo: blobInfo
          });
        }, function (err) {
          reject(err);
        });
      }
    };

    var getAllImages = function (elm) {
      return elm ? elm.getElementsByTagName('img') : [];
    };

    return function (uploadStatus, blobCache) {
      var cachedPromises = {};

      function findAll(elm, predicate) {
        var images, promises;

        if (!predicate) {
          predicate = Fun.constant(true);
        }

        images = Arr.filter(getAllImages(elm), function (img) {
          var src = img.src;

          if (!Env.fileApi) {
            return false;
          }

          if (img.hasAttribute('data-mce-bogus')) {
            return false;
          }

          if (img.hasAttribute('data-mce-placeholder')) {
            return false;
          }

          if (!src || src == Env.transparentSrc) {
            return false;
          }

          if (src.indexOf('blob:') === 0) {
            return !uploadStatus.isUploaded(src);
          }

          if (src.indexOf('data:') === 0) {
            return predicate(img);
          }

          return false;
        });

        promises = Arr.map(images, function (img) {
          var newPromise;

          if (cachedPromises[img.src]) {
            // Since the cached promise will return the cached image
            // We need to wrap it and resolve with the actual image
            return new Promise(function (resolve) {
              cachedPromises[img.src].then(function (imageInfo) {
                if (typeof imageInfo === 'string') { // error apparently
                  return imageInfo;
                }
                resolve({
                  image: img,
                  blobInfo: imageInfo.blobInfo
                });
              });
            });
          }

          newPromise = new Promise(function (resolve, reject) {
            imageToBlobInfo(blobCache, img, resolve, reject);
          }).then(function (result) {
            delete cachedPromises[result.image.src];
            return result;
          })['catch'](function (error) {
            delete cachedPromises[img.src];
            return error;
          });

          cachedPromises[img.src] = newPromise;

          return newPromise;
        });

        return Promise.all(promises);
      }

      return {
        findAll: findAll
      };
    };
  }
);