import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _regeneratorRuntime from "@babel/runtime/regenerator";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _objectSpread from "@babel/runtime/helpers/objectSpread";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import { uniqueId } from 'lodash';
import separate from './separate';
import validateType from './validateType';
import babelFile from './babelFile';
import { decode } from './sanitizeHTML';
import normalize from './normalize';
var maybeBase64String = /^[a-zA-Z0-9+/=]+$/;

var ImmutableFile =
/*#__PURE__*/
function () {
  _createClass(ImmutableFile, [{
    key: "transpiled",
    // Babel した結果. seed で与えられた時だけ設定する
    get: function get() {
      if (this._transpileCache) {
        return Promise.resolve(this._transpileCache);
      }

      return babelFile(this);
    }
  }, {
    key: "options",
    get: function get() {
      return {
        isTrashed: this.isTrashed
      };
    }
  }]);

  function ImmutableFile(seed) {
    _classCallCheck(this, ImmutableFile);

    _defineProperty(this, "id", void 0);

    _defineProperty(this, "type", void 0);

    _defineProperty(this, "text", void 0);

    _defineProperty(this, "name", void 0);

    _defineProperty(this, "path", void 0);

    _defineProperty(this, "moduleName", void 0);

    _defineProperty(this, "plain", void 0);

    _defineProperty(this, "ext", void 0);

    _defineProperty(this, "lastModified", void 0);

    _defineProperty(this, "blob", void 0);

    _defineProperty(this, "_transpileCache", void 0);

    _defineProperty(this, "isTrashed", void 0);

    if (typeof seed.name !== 'string') {
      throw new TypeError("Invalid seed: ".concat(JSON.stringify(seed)));
    }

    if (typeof seed.type !== 'string') {
      throw new TypeError("".concat(seed.name, " doesn't has 'type' property"));
    }

    this.id = uniqueId();
    this.isTrashed = Boolean(seed.options && seed.options.isTrashed);
    this.type = seed.type;
    this.text = seed.text || '';
    this.blob = seed.blob; // Use as a binary file if set

    var normalized = normalize(seed.name);

    var _separate = separate(normalized),
        name = _separate.name,
        path = _separate.path,
        moduleName = _separate.moduleName,
        plain = _separate.plain,
        ext = _separate.ext;

    this.name = name;
    this.path = path;
    this.moduleName = moduleName;
    this.plain = plain;
    this.ext = ext;
    this.lastModified = seed.lastModified || 0;
  }

  _createClass(ImmutableFile, [{
    key: "set",
    value: function set(update) {
      return new ImmutableFile(_objectSpread({}, this, update, {
        lastModified: Date.now()
      }));
    }
  }, {
    key: "compose",
    value: function () {
      var _compose = _asyncToGenerator(
      /*#__PURE__*/
      _regeneratorRuntime.mark(function _callee() {
        var seed, _transpileCache;

        return _regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                _context.t0 = this.type;
                _context.t1 = this.name;
                _context.t2 = this.lastModified;
                _context.t3 = {
                  isTrashed: this.isTrashed
                };

                if (!this.blob) {
                  _context.next = 10;
                  break;
                }

                _context.next = 7;
                return this.toDataURL();

              case 7:
                _context.t4 = _context.sent.split(',')[1];
                _context.next = 11;
                break;

              case 10:
                _context.t4 = this.text;

              case 11:
                _context.t5 = _context.t4;
                seed = {
                  type: _context.t0,
                  name: _context.t1,
                  lastModified: _context.t2,
                  options: _context.t3,
                  composed: _context.t5
                };
                _context.next = 15;
                return Promise.race([this.transpiled, wait(100) // 100ms 以内
                ]);

              case 15:
                _transpileCache = _context.sent;

                if (_transpileCache) {
                  seed._transpileCache = _transpileCache;
                }

                return _context.abrupt("return", seed);

              case 18:
              case "end":
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function compose() {
        return _compose.apply(this, arguments);
      }

      return compose;
    }()
  }, {
    key: "toDataURL",
    value: function toDataURL() {
      var _this = this;

      var cache = ImmutableFile.dataURLCache.get(this);
      if (cache) return cache;
      var promise = new Promise(function (resolve) {
        var reader = new FileReader();

        reader.onload = function () {
          var dataURL = reader.result;
          resolve(dataURL);
        };

        var blob = _this.blob || new Blob([_this.text], {
          type: _this.type
        });

        if (!blob) {
          throw new Error("Failed to make Blob of ".concat(_this.name));
        }

        reader.readAsDataURL(blob);
      });
      ImmutableFile.dataURLCache.set(this, promise);
      return promise;
    }
  }, {
    key: "is",
    value: function is(name) {
      return validateType(name, this.type);
    }
  }, {
    key: "rename",
    value: function rename(newName) {
      var path = this.path,
          ext = this.ext;
      return this.set({
        name: path + newName + ext
      });
    }
  }, {
    key: "move",
    value: function move(newPath) {
      if (newPath.lastIndexOf('/') !== newPath.length - 1) {
        newPath += '/';
      }

      return this.set({
        name: newPath + this.plain + this.ext
      });
    }
  }, {
    key: "component",
    get: function get() {
      console.trace();
      throw new Error('ImmutableFile.component is unsupported');
    }
  }, {
    key: "json",
    get: function get() {
      console.trace();
      throw new Error('ImmutableFile.json is not supported');
    }
  }], [{
    key: "fromSeed",
    value: function fromSeed(seed) {
      var _composed = seed.composed,
          _transpileCache = seed._transpileCache,
          props = _objectWithoutProperties(seed, ["composed", "_transpileCache"]); // 歴史的な理由で HTML サニタイズされていることがある


      var composed = _composed.substr(0, 5) === '<!--\n' && _composed.substr(-4) === '\n-->' ? decode(_composed) : _composed;

      if (validateType('blob', props.type)) {
        // base64 encode された文字列を Blob にする
        var bin = atob(composed);
        var byteArray = new Uint8Array(bin.length);

        for (var i = bin.length - 1; i >= 0; i--) {
          byteArray[i] = bin.charCodeAt(i);
        }

        var _blob = new Blob([byteArray.buffer], {
          type: seed.type
        });

        return new ImmutableFile(_objectSpread({}, props, {
          blob: _blob
        }));
      } else {
        // 歴史的な理由で URI encode & base64 encode されたりされなかったりする
        var tryParse = function tryParse() {
          if (!maybeBase64String.test(composed)) return composed; // base64 string ではない

          try {
            console.log('decoding', props.name, 'composed:', composed.substr(0, 20));

            var _text = decodeURIComponent(escape(atob(composed)));

            console.log('successfuly decoded', _text.substr(0, 20));
            return _text;
          } catch (error) {
            console.error(error);
          }

          return '';
        };

        var file = new ImmutableFile(_objectSpread({}, props, {
          text: tryParse()
        }));

        if (_transpileCache) {
          // precompiled javascript code を入れて babel をスキップ
          file._transpileCache = _transpileCache;
        }

        return file;
      }
    }
  }, {
    key: "fromSeeds",
    value: function fromSeeds(seeds) {
      return seeds.map(function (seed) {
        try {
          return ImmutableFile.fromSeed(seed);
        } catch (error) {
          console.error(error);
          return undefined;
        }
      }).filter(function (file) {
        return file;
      });
    }
  }, {
    key: "fromFile",
    value: function fromFile(file) {
      if (validateType('blob', file.type)) {
        return Promise.resolve(new ImmutableFile({
          type: file.type,
          name: file.name,
          blob: file,
          lastModified: file.lastModified
        }));
      } else {
        return new Promise(function (resolve) {
          var reader = new FileReader();

          reader.onload = function (e) {
            resolve(new ImmutableFile({
              type: file.type,
              name: file.name,
              text: reader.result,
              lastModified: file.lastModified
            }));
          };

          reader.readAsText(file);
        });
      }
    }
  }]);

  return ImmutableFile;
}();

_defineProperty(ImmutableFile, "dataURLCache", new WeakMap());

export { ImmutableFile as default };

function wait() {
  var ms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1000;
  return new Promise(function (resolve) {
    return setTimeout(function () {
      return resolve();
    }, ms);
  });
}