/*
 * grunt-contrib-uglify
 * https://gruntjs.com/
 *
 * Copyright (c) 2017 "Cowboy" Ben Alman, contributors
 * Licensed under the MIT license.
 */

'use strict';

// External libs.
var path = require('path');
var UglifyJS = require('uglify-js');
var uriPath = require('uri-path');
var domprops = require('uglify-js/tools/domprops');

// Converts \r\n to \n
function normalizeLf(string) {
  return string.replace(/\r\n/g, '\n');
}

function toCache(cache, key) {
  if (cache[key]) {
    cache[key].props = UglifyJS.Dictionary.fromObject(cache[key].props);
  } else {
    cache[key] = {
      cname: -1,
      props: new UglifyJS.Dictionary()
    };
  }
  return cache[key];
}

exports.init = function(grunt) {
  var exports = {};

  // Minify with UglifyJS.
  // From https://github.com/mishoo/UglifyJS2
  exports.minify = function(files, dest, options) {
    options = options || {};

    grunt.verbose.write('Minifying with UglifyJS...');

    var totalCode = '';
    var minifyOptions = {
      compress: options.compress,
      ie8: options.ie8,
      keep_fnames: options.keep_fnames,
      mangle: options.mangle,
      output: options.output || {},
      parse: options.parse || {},
      sourceMap: options.sourceMap,
      toplevel: options.toplevel,
      wrap: options.wrap
    };

    if (options.banner) {
      minifyOptions.output.preamble = normalizeLf(options.banner);
    }

    if (options.beautify) {
      minifyOptions.output.beautify = true;
      for (var key in options.beautify) {
        minifyOptions.output[key] = options.beautify[key];
      }
    }

    var cache;
    if (options.nameCache) {
      try {
        cache = JSON.parse(grunt.file.read(options.nameCache));
      } catch (ex) {
        cache = {};
      }
    }

    if (minifyOptions.mangle) {
      if (typeof minifyOptions.mangle !== 'object') {
        minifyOptions.mangle = {};
      }
      if (cache) {
        minifyOptions.mangle.cache = toCache(cache, 'vars');
      }
      if (!Array.isArray(minifyOptions.mangle.reserved)) {
        minifyOptions.mangle.reserved = [];
      }
      if (minifyOptions.mangle.properties) {
        if (typeof minifyOptions.mangle.properties !== 'object') {
          minifyOptions.mangle.properties = {};
        }
        if (cache) {
          minifyOptions.mangle.properties.cache = toCache(cache, 'props');
        }
        if (!Array.isArray(minifyOptions.mangle.properties.reserved)) {
          minifyOptions.mangle.properties.reserved = [];
        }
        if (options.reserveDOMProperties) {
          domprops.forEach(function(name) {
            UglifyJS._push_uniq(minifyOptions.mangle.properties.reserved, name);
          });
        }
      }
      if (options.exceptionsFiles) {
        options.exceptionsFiles.forEach(function(file) {
          try {
            var obj = JSON.parse(grunt.file.read(file));
            if (minifyOptions.mangle && obj.vars) {
              obj.vars.forEach(function(name) {
                UglifyJS._push_uniq(minifyOptions.mangle.reserved, name);
              });
            }
            if (minifyOptions.mangle.properties && obj.props) {
              obj.props.forEach(function(name) {
                UglifyJS._push_uniq(minifyOptions.mangle.properties.reserved, name);
              });
            }
          } catch (ex) {
            grunt.warn(ex);
          }
        });
      }
    }

    var result = UglifyJS.minify(files.reduce(function(o, file) {
      var code = grunt.file.read(file);
      totalCode += code;

      // The src file name must be relative to the source map for things to work
      var basename = path.basename(file);
      var fileDir = path.dirname(file);
      var sourceMapDir = path.dirname(options.generatedSourceMapName);
      var relativePath = path.relative(sourceMapDir, fileDir);
      var pathPrefix = relativePath ? relativePath + path.sep : '';

      // Convert paths to use forward slashes for sourcemap use in the browser
      o[uriPath(pathPrefix + basename)] = code;
      return o;
    }, {}), minifyOptions);
    if (result.error) {
      throw result.error;
    }

    if (options.nameCache) {
      grunt.file.write(options.nameCache, JSON.stringify(cache, function(key, value) {
        return value instanceof UglifyJS.Dictionary ? value.toObject() : value;
      }));
    }

    grunt.verbose.ok();

    return {
      max: totalCode,
      min: result.code,
      sourceMap: result.map
    };
  };

  return exports;
};
