module.exports = function(grunt) {

// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);

// Time how long tasks take. Can help when optimizing build times
try {
  require('time-grunt')(grunt);
} catch(e) {

}

var userDocsPath = process.env.USER_DOCS_PATH,
    pluginPaths  = process.env.PLUGIN_PATHS ? process.env.PLUGIN_PATHS.split(':') : [],
    buildPath    = userDocsPath + '/output',
    browserPort  = process.env.DOC_PORT || '9090',
    bowerPaths   = pluginPaths.concat([userDocsPath]).map(function(f) { return f + '/bower.json'; }),
    basePath     = process.env.BASE_PATH || '';

var cssOutput = {};
cssOutput[buildPath + '/css/main.css'] = userDocsPath + '/styles.scss';

grunt.registerMultiTask('merge_bower', function() {
  var merge = require('package-merge'),
      options = this.options({
        template: {},
        writePath: './bower.json',
        install: false
      }),
      shouldInstallDevAssets = this.flags.development,
      files = [],
      template = options.templatePath ? grunt.file.read(options.templatePath) : JSON.stringify(options.template, null, 2),
      dependencies = template.dependencies || {},
      devDependencies = template.devDependencies || {};

  this.files.forEach(function(obj) {
    files = files.concat(obj.src.filter(function(file) {
      return grunt.file.exists(file);
    }));
  });

  var result = files.reduce(function(combined, file) {
    grunt.verbose.writelns("Combining " + file);
    return merge(grunt.file.read(file), combined);
  }, template);

  grunt.file.write(options.writePath, result);

  if (options.install) {
    grunt.log.writeln('Installing Bower Dependencies');
    var exec = require('child_process').exec;
    var done = this.async();
    exec('node_modules/.bin/bower install --force-latest' + (shouldInstallDevAssets ? '' : ' --production') + '--config.interactive=false --config.analytics=false', { cwd: '.' }, function(error, result) {
      if (error) {
        grunt.log.error('bower install failed with' + error);
        done(false);
      } else {
        grunt.log.ok('Dependencies installed succesfully');
        done();
      }
    });
  }
});

grunt.initConfig({
  // web server for development
  connect: {
    options: {
      port: browserPort,
      hostname: '0.0.0.0'
    },
    livereload: {
      options: {
        livereload: 9091,
        open: true,
        base: [userDocsPath].concat(pluginPaths).concat([,
          '.tmp',
          '.data',
          'app'
        ]),
        // This is to handle support of using $locationProvider.html5Mode(true).
        middleware: function(connect, options, middlewares) {
          middlewares.push(function(req, res, next) {
            var segments = require('url').parse(req.url).pathname.split('/'),
                last = segments[segments.length - 1];
            if (last.split('.').length === 1) {
              try {
                var index = grunt.file.read('.tmp/index.html');
                res.setHeader("Content-Type", "text/html");
                res.write(index, 'utf8');
                res.end()
              } catch(e) {
                next(e);
              }
            } else {
              next();
            }
          });

          return middlewares;
        }
      }
    },
    dist: {
      options: {
        base: ['.data', buildPath]
      }
    }
  },

  clean: {
    serve: {
      files: [{
        dot: true,
        src: [
          '.tmp'
        ]
      }]
    },
    dist: {
      options: {
        force: true,
      },
      files: [{
        dot: true,
        src: [
          '.tmp',
          buildPath
        ]
      }]
    }
  },
  // Automatically inject Bower components into the app
  wiredep: {
    app: {
      src: '.tmp/index.html',
      ignorePath: /(..\/)?app\//,
      // We do not use bootstrap's JS code, also the SASS styles are included by our style sheet
      exclude: [/bootstrap-sass/]
    }
  },

  // Automatically inject the JS files into the app
  fileblocks: {
    options: { removeFiles: true },
    dist: {
      src: 'app/index.html',
      dest: '.tmp/index.html',
      blocks: {
        scripts: {
          cwd: 'app',
          // This ensures app.js comes first so the angular module is declared,
          // then Praxis core files, then plugin files
          src: ['js/app.js', 'js/**/*.js']
        },
        pluginscripts: {
          src: pluginPaths.map(function(path) {
            return path + '/**/*.js';
          })
        },
        userscripts: {
          // cwd: userDocsPath,
          src: [userDocsPath + '/app.js', userDocsPath + '/**/*.js', '!' + userDocsPath + '/output/**/*.js']
        }
      }
    },

    serve: {
      src: 'app/index.html',
      dest: '.tmp/index.html',
      blocks: {
        scripts: {
          cwd: 'app',
          // This ensures app.js comes first so the angular module is declared
          src: ['js/app.js', 'js/**/*.js']
        },
        pluginscripts: {
          src: pluginPaths.map(function(path) {
            return path + '/**/*.js';
          }),
          templatesFn: {
            js: function(file) {
              var pluginPath = pluginPaths.filter(function(path) {
                return grunt.file.isMatch(path + '/**/*.js', file);
              })[0];

              return file.replace(pluginPath + '/', '');
            }
          }
        },
        userscripts: {
          cwd: userDocsPath,
          src: ['app.js', '**/*.js', '!output/**/*.js']
        }
      }
    }
  },

  // Build stylesheet from SASS files
  sass: {
    server: {
      options: {
        sourceComments: 'map',
        sourceMap: __dirname  + '/.tmp/css/main.css.map',
        includePaths: ['app/sass'].concat(pluginPaths)
      },
      files: {
        '.tmp/css/main.css': userDocsPath + '/styles.scss'
      }
    },
    dist: {
      options: {
        sourceComments: 'none',
        includePaths: ['app/sass'].concat(pluginPaths)
      },
      files: cssOutput
    }
  },

  cssmin: {
    dist: {
      files: [{
        src: buildPath + '/css/main.css',
        dest: buildPath + '/css/main.css'
      }]
    }
  },

  ngAnnotate: {
    options: {
      add: true,
      singleQuotes: true,
      ngAnnotateOptions: {
        plugin: [{init: function() {},
        match: function(node) {
          if (node.callee && node.callee.object) {
            var object = node.callee.object;
            if (object && object.type === 'Identifier' && object.name === 'templateForProvider' && node.callee.property.name === 'register') {
              return node.arguments[0];
            }
            if (object && object.type === 'Identifier' && object.name === 'ExamplesProvider' && node.callee.property.name === 'register') {
              return node.arguments[2];
            }
          }
        }}]
      }
    },
    dist: {
      files: [{
        expand: true,
        cwd: '.tmp/concat/scripts',
        src: '*.js',
        dest: '.tmp/concat/scripts'
      }]
    }
  },

  filerev: {
    dist: {
      src: [
        buildPath + '/scripts/*.js',
        buildPath + '/css/*.css'
      ]
    }
  },

  ngtemplates: {
    options: {
      htmlmin: {
        collapseBooleanAttributes: true,
        collapseWhitespace: true,
        removeAttributeQuotes: true,
        removeComments: true,
        removeEmptyAttributes: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true
      },
      bootstrap: function(module, script) {
        return 'angular.module("' + module + '").run(function($templateCache){' + script + '});';
      },
    },
    dist: {
      files: [{
        cwd: 'app',
        src: 'views/**/*.html',
        dest: '.tmp/templates.js'
      }].concat(pluginPaths.map(function (path, i) {
        return {
          cwd: path,
          src: 'views/**/*.html',
          dest: '.tmp/plugin-templates-' + i + '.js'
        };
      })),

      options: {
        usemin: buildPath + '/scripts/praxis.js',
        module: 'PraxisDocBrowser',
      }
    },
    userScripts: {
      cwd: userDocsPath,
      src: 'views/**/*.html',
      dest: '.tmp/usertemplates.js',
      options: {
        usemin: buildPath + '/scripts/docs.js',
        module: 'DocBrowser',
      }
    }
  },

  copy: {
    dist: {
      files: [{
        expand: true,
        dot: true,
        cwd: 'app',
        dest: buildPath,
        src: [
          '*.{ico,png,txt}'
        ]
      }, {
        expand: true,
        dot: true,
        cwd: '.tmp',
        dest: buildPath,
        src: [
          'index.html'
        ]
      }, {
        expand: true,
        dot: true,
        cwd: userDocsPath,
        dest: buildPath,
        src: [
          '**/*',
          '!**/*.{js,scss,sass,less,coffee}',
          '!views/**',
          '!**/.*',
          '!output/**',
          '!bower.json'
        ]
      }].concat(pluginPaths.map(function(path) {
        return {
          expand: true,
          dot: true,
          cwd: path,
          dest: buildPath,
          src: [
            '**/*',
            '!**/*.{js,scss,sass,less,coffee}',
            '!views/**',
            '!**/.*',
            '!output/**',
            '!bower.json'
          ]
        };
      }))
    }
  },

  merge_bower: {
    app: {
      options: {
        writePath: 'bower.json',
        templatePath: 'bower_template.json',
        install: true
      },
      src: bowerPaths
    }
  },

  // Reads HTML for usemin blocks to enable smart builds that automatically
  // concat, minify and revision files. Creates configurations in memory so
  // additional tasks can operate on them
  useminPrepare: {
    html: '.tmp/index.html',
    options: {
      dest: buildPath
    }
  },

  // Performs rewrites based on rev and the useminPrepare configuration
  usemin: {
    html: [buildPath + '/index.html'],
    css: [buildPath + '/css/{,*/}*.css'],
    options: {
      assetsDirs: [buildPath],
      blockReplacements: {
        base: function(block) {
          if (basePath !== '') {
            return '<base href="' + basePath + '">';
          }
          return '';
        }
      }
    }
  },

  karma: {
    options: {
      files: [
        'node_modules/quick_check/dist/jasmine-quick-check.js',
        'app/bower_components/jquery/dist/jquery.js',
        'app/bower_components/angular/angular.js',
        'app/bower_components/lodash/lodash.js',
        'app/bower_components/angular-ui-router/release/angular-ui-router.js',
        'app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.js',
        'app/bower_components/angular-sanitize/angular-sanitize.js',
        'app/bower_components/angular-mocks/angular-mocks.js',
        'app/bower_components/showdown/compressed/Showdown.min.js',
        'app/js/app.js', 'app/js/**/*.js', '.tmp/templates.js', '../../spec/api_browser/**/*.js'
      ],
      frameworks: ['jasmine'],
      reporters: ['dots'],
    },
    unit: {
      browsers: ['PhantomJS'],
      singleRun: true
    }
  },

  jshint: {
    src: ['app/js/**/*.js', '../../spec/api_browser/**/*.js'],
    options: {
      bitwise: true,
      immed: true,
      newcap: false,
      noarg: true,
      noempty: true,
      nonew: true,
      trailing: true,
      boss: true,
      eqnull: true,
      expr: true,
      laxbreak: true,
      loopfunc: true,
      sub: true,
      undef: true,
      unused: true,
      browser: true,
      quotmark: true,
      indent: 2,
      jasmine: true,
      globals: {
        "angular": false,
        "app": true,
        "_": false,
        "$": false,
        "jQuery": false,
        "Showdown": false,
        "inject": false,
        "qc": false
      }
    }
  }
});

grunt.registerTask('runGenerator', function() {
  var exec = require('child_process').exec;
  var done = this.async();
  exec('bundle exec rake praxis:docs:generate', {cwd: userDocsPath + '/../'}, done);
});

grunt.registerTask('serve', function(target) {
  if (target === 'dist') {
    return grunt.task.run(['build', 'connect:dist:keepalive']);
  }
  grunt.config.merge({
    watch: {
      // Updates index.html for any file added or removed
      scripts: {
        files: ['app/js/**/*.js', userDocsPath + '/**/*.js'],
        tasks: ['fileblocks:serve', 'wiredep'],
        options: { livereload: 9091 }
      },

      // Updates index.html for any bower component added or removed
      bowerComponents: {
        files: ['app/bower_components/**/.bower.json'],
        tasks: 'wiredep',
        options: { livereload: 9091 }
      },

      // watches bower paths to retrigger bower install
      bower: {
        files: bowerPaths.concat(['bower_template.json']),
        tasks: ['merge_bower:app', 'wiredep'],
        options: { livereload: 9091 }
      },

      // Rebuild the stylesheets for any SASS file changed
      sass: {
        files: [
          'app/sass/**/*.scss',
          'app/bower_components/**/*.scss',
          userDocsPath + '/**/*.scss'
        ],
        tasks: 'sass',
        options: { livereload: 9091 }
      },

      data: {
        files: [
          userDocsPath + '/../design/**/*.rb'
        ],
        tasks: 'runGenerator',
        options: { livereload: 9091 }
      },

      // Watches files that don't need processing
      other: {
        files: [
          'app/css/*.css',
          "app/index.html",
          "app/views/**/*.html",
          userDocsPath + '/views/**/*.html'
        ],
        options: {
          livereload: 9091
        }
      }
    }
  });

  grunt.task.run([
    'clean:serve',
    'merge_bower:app',
    'fileblocks:serve',
    'wiredep',
    'sass:server',
    'connect:livereload',
    'watch'
  ]);
});

grunt.registerTask('build', [
  'clean:dist',
  'merge_bower:app',
  'fileblocks:dist',
  'wiredep',
  'sass:dist',
  'useminPrepare',
  'ngtemplates',
  'concat',
  'ngAnnotate',
  'copy:dist',
  'cssmin',
  'uglify',
  'filerev',
  'usemin'
]);

grunt.registerTask('test', function() {
  grunt.config.set('watch', {
    tests: {
      files: [
        'app/js/**',
        '../../spec/api_browser/**'
      ],
      tasks: ['jshint:src', 'karma:unit'],
      options: {
        atBegin: true
      }
    }
  });

  grunt.task.run([
    'ngtemplates:dist',
    'watch:tests'
  ]);
});

grunt.registerTask('ci', [
  'merge_bower:app:development',
  'ngtemplates:dist',
  'jshint:src',
  'karma:unit'
]);

grunt.registerTask("default", ["serve"]);

};