/*
 * grunt-chmod
 * https://github.com/JamesMGreene/grunt-chmod
 *
 * Copyright (c) 2013 James M. Greene
 * Licensed under the MIT license.
 */

'use strict';

var fs = require('fs');

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    jshint: {
      options: {
        jshintrc: '.jshintrc',
      },
      all: [
        'Gruntfile.js',
        'tasks/**/*.js',
        'test/**/*.js',
      ]
    },

    // Before generating any new files, remove any previously-created files.
    clean: {
      tests: ['tmp'],
    },

    // Configuration to be run (and then tested).
    chmod: {
      // Emit events, JUST for testing purposes!
      options: {
        emit: true
      },
      default_options: {
        src: ['tmp/default_options.txt']
      },
      custom_options_without_string_mode: {
        options: {
          mode: {}
        },
        src: ['tmp/custom_options_without_string_mode.txt']
      },
      custom_options_with_empty_mode: {
        options: {
          mode: ''
        },
        src: ['tmp/custom_options_with_empty_mode.txt']
      },
      custom_options_with_invalid_mode: {
        options: {
          mode: 'a+rwx'
        },
        src: ['tmp/custom_options_with_invalid_mode.txt']
      },
      custom_options_nonexistent_file: {
        options: {
          mode: '600'
        },
        src: ['tmp/custom_options_nonexistent_file.txt']
      },
      custom_options_file: {
        options: {
          /* Because it seems Windows will set the U digit (UGO) for all spots, so '400' => '444'. Default on Windows is: '666' */
          mode: '444'
        },
        src: ['tmp/custom_options_file.js']
      },
      custom_options_dir: {
        options: {
          /* Because it seems Windows will set the U digit (UGO) for all spots, so '400' => '444'. Default on Windows is: '666' */
          mode: '444'
        },
        src: ['tmp/custom_options_dir/']
      },
      custom_options_file_symbolic_1: {
        options: {
          mode: 'u+r' // 400
        },
        src: ['tmp/custom_options_file_symbolic_1.js']
      },
      custom_options_file_symbolic_2: {
        options: {
          mode: 'u+rw' // 600
        },
        src: ['tmp/custom_options_file_symbolic_2.js']
      },
      custom_options_file_symbolic_3: {
        options: {
          mode: 'u+rwx' // 700
        },
        src: ['tmp/custom_options_file_symbolic_3.js']
      },

      custom_options_file_symbolic_4: {
        options: {
          mode: 'uo+r' // 404
        },
        src: ['tmp/custom_options_file_symbolic_4.js']
      },

    },

    // Unit tests.
    nodeunit: {
      all: ['test/*_test.js']
    }

  });

  // Actually load this plugin's task(s).
  grunt.loadTasks('tasks');

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-nodeunit');

  var forceValue = (function(val) {
    return (typeof val === 'boolean' ? val : !!val);
  })(grunt.option('force'));
  
  // Enable force for tests to avoid the build halting on warnings
  grunt.registerTask('force-enable', function() {
    grunt.option('force', true);
  });
  grunt.registerTask('force-revert', function() {
    grunt.option('force', forceValue);
  });
  
  // Test setup
  grunt.registerTask('test-setup', function() {
    grunt.file.mkdir('tmp/custom_options_dir');
    grunt.file.write('tmp/custom_options_file.js', '');

    grunt.file.write('tmp/custom_options_file_symbolic_1.js', '');
    grunt.file.write('tmp/custom_options_file_symbolic_2.js', '');
    grunt.file.write('tmp/custom_options_file_symbolic_3.js', '');
    grunt.file.write('tmp/custom_options_file_symbolic_4.js', '');

	fs.chmodSync('tmp/custom_options_file_symbolic_1.js', '000');
	fs.chmodSync('tmp/custom_options_file_symbolic_2.js', '000');
	fs.chmodSync('tmp/custom_options_file_symbolic_3.js', '000');
	fs.chmodSync('tmp/custom_options_file_symbolic_4.js', '000');
	

  });
  
  // Test emission listeners
  var args = (function() {
    var slicer = Array.prototype.slice;
    return function(argsObj) {
      return slicer.call(argsObj, 0);
    };
  })();
  var emissions = [],
      taskTargetName;
  grunt.registerTask('listeners-on', function() {
    grunt.event.on('chmod.*', function(msg) {
      if (this.event === 'chmod.taskTargetName') {
        taskTargetName = msg;
        return;
      }
      var emission = {
        name: this.event,
        args: args(arguments)
      };
      emissions.push(emission);
    });
  });
  grunt.registerTask('listeners-off', function() {
    grunt.file.write('tmp/' + taskTargetName + '.json', JSON.stringify(emissions, null, 2));
    
    emissions.length = 0;
    grunt.event.removeAllListeners('chmod.*');
  });
  
  var badConfigOptions = [
        'default_options',
        'custom_options_without_string_mode', 
        'custom_options_with_empty_mode',
        'custom_options_with_invalid_mode',
        'custom_options_nonexistent_file'
      ];
  badConfigOptions.forEach(function(e, i) {
    grunt.registerTask(
      'test-prep-' + (i + 1),
      [
        'force-enable',
        'listeners-on',
        'chmod:' + e,
        'listeners-off',
        'force-revert'
      ]
    );
  });
  var goodConfigOptions = [
        'custom_options_file',
        'custom_options_dir',
		'custom_options_file_symbolic_1',
		'custom_options_file_symbolic_2',
		'custom_options_file_symbolic_3',
		'custom_options_file_symbolic_4'
      ];
  goodConfigOptions.forEach(function(e, i) {
    grunt.registerTask(
      'test-prep-' + (badConfigOptions.length + i + 1),
      [
        'listeners-on',
        'chmod:' + e,
        'listeners-off'
      ]
    );
  });

  // Whenever the "test" task is run, first clean the "tmp" dir, then run this plugin's
  // task(s), then test the result(s). Do NOT clean the "tmp" dir at the end!
  grunt.registerTask(
    'test',
    (function() {
      var taskList = [];
      taskList.push('clean');
      taskList.push('test-setup');
      for (var i = 1, len = (badConfigOptions.length + goodConfigOptions.length + 1); i < len; i++) {
        taskList.push('test-prep-' + i);
      }
      taskList.push('nodeunit');
      return taskList;
    })()
  );

  // By default: lint, run all tests, and clean it up.
  grunt.registerTask('default', ['jshint', 'test', 'clean']);
  // Travis-CI: lint, run all tests, and clean it up.
  grunt.registerTask('travis', ['jshint', 'test', 'clean']);
};
