(function() {
  var MapGenerator, Result, base64js, fs, lazy, mozilla, path;

  base64js = require('base64-js');

  mozilla = require('source-map');

  Result = require('./result');

  lazy = require('./lazy');

  path = require('path');

  fs = require('fs');

  MapGenerator = (function() {
    function MapGenerator(root, opts) {
      this.root = root;
      this.opts = opts;
    }

    MapGenerator.prototype.startWith = function(string, start) {
      return string.slice(0, +(start.length - 1) + 1 || 9e9) === start;
    };

    MapGenerator.prototype.isMap = function() {
      if (typeof this.opts.map === 'boolean') {
        return this.opts.map;
      }
      return !!this.opts.inlineMap || !!this.prevMap();
    };

    lazy(MapGenerator, 'isInline', function() {
      if (this.opts.inlineMap != null) {
        return this.opts.inlineMap;
      }
      return this.isPrevInline();
    });

    lazy(MapGenerator, 'isPrevInline', function() {
      var text;
      if (!this.prevAnnotation()) {
        return false;
      }
      text = this.prevAnnotation().text;
      return this.startWith(text, '# sourceMappingURL=data:');
    });

    lazy(MapGenerator, 'prevMap', function() {
      var file, map;
      if (this.opts.map && typeof this.opts.map !== 'boolean') {
        return this.opts.map;
      }
      if (this.isPrevInline()) {
        return this.encodeInline(this.prevAnnotation().text);
      } else if (this.opts.from) {
        map = this.opts.from + '.map';
        if (this.prevAnnotation()) {
          file = this.prevAnnotation().text.replace('# sourceMappingURL=', '');
          map = path.join(path.dirname(this.opts.from), file);
        }
        if (typeof fs.existsSync === "function" ? fs.existsSync(map) : void 0) {
          return fs.readFileSync(map).toString();
        } else {
          return false;
        }
      }
    });

    lazy(MapGenerator, 'prevAnnotation', function() {
      var last;
      last = this.root.last;
      if (!last) {
        return null;
      }
      if (last.type === 'comment' && this.startWith(last.text, '# sourceMappingURL=')) {
        return last;
      } else {
        return null;
      }
    });

    MapGenerator.prototype.encodeInline = function(text) {
      var base64, byte, bytes, uri;
      uri = '# sourceMappingURL=data:application/json,';
      base64 = '# sourceMappingURL=data:application/json;base64,';
      if (this.startWith(text, uri)) {
        return decodeURIComponent(text.slice(uri.length));
      } else if (this.startWith(text, base64)) {
        text = text.slice(base64.length);
        bytes = base64js.toByteArray(text);
        return ((function() {
          var _i, _len, _results;
          _results = [];
          for (_i = 0, _len = bytes.length; _i < _len; _i++) {
            byte = bytes[_i];
            _results.push(String.fromCharCode(byte));
          }
          return _results;
        })()).join('');
      } else {
        throw new Error('Unknown source map encoding');
      }
    };

    MapGenerator.prototype.clearAnnotation = function() {
      var _ref;
      return (_ref = this.prevAnnotation()) != null ? _ref.removeSelf() : void 0;
    };

    MapGenerator.prototype.applyPrevMap = function() {
      var from, prev;
      if (this.prevMap()) {
        prev = this.prevMap();
        prev = typeof prev === 'string' ? JSON.parse(prev) : prev instanceof mozilla.SourceMapConsumer ? mozilla.SourceMapGenerator.fromSourceMap(prev).toJSON() : typeof prev === 'object' && prev.toJSON ? prev.toJSON() : prev;
        prev = new mozilla.SourceMapConsumer(prev);
        from = this.relative(this.opts.from);
        return this.map.applySourceMap(prev, from, path.dirname(from));
      }
    };

    MapGenerator.prototype.addAnnotation = function() {
      var bytes, char, content;
      if (this.opts.mapAnnotation === false) {
        return;
      }
      if (this.prevMap() && !this.prevAnnotation()) {
        return;
      }
      content = this.isInline() ? (bytes = (function() {
        var _i, _len, _ref, _results;
        _ref = this.map.toString();
        _results = [];
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          char = _ref[_i];
          _results.push(char.charCodeAt(0));
        }
        return _results;
      }).call(this), "data:application/json;base64," + base64js.fromByteArray(bytes)) : this.outputFile() + '.map';
      return this.css += "\n/*# sourceMappingURL=" + content + " */";
    };

    MapGenerator.prototype.outputFile = function() {
      if (this.opts.to) {
        return path.basename(this.opts.to);
      } else {
        return 'to.css';
      }
    };

    MapGenerator.prototype.generateMap = function() {
      this.stringify();
      this.applyPrevMap();
      this.addAnnotation();
      if (this.isInline()) {
        return new Result(this.root, this.css);
      } else {
        return new Result(this.root, this.css, this.map.toString());
      }
    };

    MapGenerator.prototype.relative = function(file) {
      var from;
      from = this.opts.to ? path.dirname(this.opts.to) : '.';
      file = path.relative(from, file);
      if (path.sep === '\\') {
        file = file.replace('\\', '/');
      }
      return file;
    };

    MapGenerator.prototype.sourcePath = function(node) {
      return this.relative(node.source.file || 'from.css');
    };

    MapGenerator.prototype.stringify = function() {
      var builder, column, line;
      this.css = '';
      this.map = new mozilla.SourceMapGenerator({
        file: this.outputFile()
      });
      line = 1;
      column = 1;
      builder = (function(_this) {
        return function(str, node, type) {
          var last, lines, _ref, _ref1;
          _this.css += str;
          if ((node != null ? (_ref = node.source) != null ? _ref.start : void 0 : void 0) && type !== 'end') {
            _this.map.addMapping({
              source: _this.sourcePath(node),
              original: {
                line: node.source.start.line,
                column: node.source.start.column - 1
              },
              generated: {
                line: line,
                column: column - 1
              }
            });
          }
          lines = str.match(/\n/g);
          if (lines) {
            line += lines.length;
            last = str.lastIndexOf("\n");
            column = str.length - last;
          } else {
            column = column + str.length;
          }
          if ((node != null ? (_ref1 = node.source) != null ? _ref1.end : void 0 : void 0) && type !== 'start') {
            return _this.map.addMapping({
              source: _this.sourcePath(node),
              original: {
                line: node.source.end.line,
                column: node.source.end.column
              },
              generated: {
                line: line,
                column: column
              }
            });
          }
        };
      })(this);
      return this.root.stringify(builder);
    };

    MapGenerator.prototype.getResult = function() {
      this.clearAnnotation();
      if (this.isMap()) {
        return this.generateMap();
      } else {
        return new Result(this.root);
      }
    };

    return MapGenerator;

  })();

  module.exports = MapGenerator;

}).call(this);
