/*global module:false*/
module.exports = function( grunt ) {
	'use strict';

	var pkg = grunt.file.readJSON( 'package.json' ),
		tasks,

		defaults = {
			dependencies : {
				widgets: {
					'saveSort resizable pager filter columnSelector' : 'storage',
					'filter-formatter-html5 filter-formatter-jui filter-formatter-select2' : 'filter'
					// 'stickyHeaders' : 'addResizeEvent' // included with stickyHeaders widget (for now)
				}
			},
			standardWidgets: [
				'storage', // req by saveSort; optional in others
				'uitheme',
				'columns',
				'filter',
				'stickyHeaders',
				'resizable',
				'saveSort'
				// 'addResizeEvent', // included with stickyHeaders widget
				// 'zebra' // included in core
			],
			standardWidgetFileName: 'jquery.tablesorter.widgets.js',
			wrappers : {
				widgetPrefix: 'js/widgets/widget-',
				widgetSuffix: '.js',
				parserPrefix: 'js/parsers/parser-',
				parserSuffix: '.js'
			},
			wrapperUMD: {
				// tablesorter core, widgets & parsers
				coreBanner: '(function(factory){' +
					"if (typeof define === 'function' && define.amd){" +
							"define(['jquery'], factory);" +
						"} else if (typeof module === 'object' && typeof module.exports === 'object'){" +
							"module.exports = factory(require('jquery'));" +
						'} else {' +
							'factory(jQuery);' +
						'}' +
					'}(function(jQuery){\n',
				// widgets wrapper & combined wrapper - may need a separate entry when this wrapper is redefined to make
				// widgets a dependency of the core (see https://github.com/Mottie/tablesorter/issues/855)
				banner: '<%= pkg.banner %>/* Includes widgets ( <%= pkg.selectedWidgets %> <%= pkg.selectedParsers %>) */\n' +
					'(function(factory){' +
						"if (typeof define === 'function' && define.amd){" +
							"define(['jquery'], factory);" +
						"} else if (typeof module === 'object' && typeof module.exports === 'object'){" +
							"module.exports = factory(require('jquery'));" +
						'} else {' +
							'factory(jQuery);' +
						'}' +
					'}(function(jQuery) {\n',
				footer: 'return jQuery.tablesorter;}));\n',
				footerMain: 'return jQuery;}));\n'
			},
			noModBanner: '/*** This file is dynamically generated ***\n' +
				'█████▄ ▄████▄   █████▄ ▄████▄ ██████   ███████▄ ▄████▄ █████▄ ██ ██████ ██  ██\n' +
				'██  ██ ██  ██   ██  ██ ██  ██   ██     ██ ██ ██ ██  ██ ██  ██ ██ ██▄▄   ██▄▄██\n' +
				'██  ██ ██  ██   ██  ██ ██  ██   ██     ██ ██ ██ ██  ██ ██  ██ ██ ██▀▀    ▀▀▀██\n' +
				'█████▀ ▀████▀   ██  ██ ▀████▀   ██     ██ ██ ██ ▀████▀ █████▀ ██ ██     █████▀\n*/\n'
		},

		// example widgets = [ 'pager', 'column', 'filter', 'stickyHeaders' ];
		addWidgetDependencies = function( widgets ) {
			var indx, dep,
				maxDeps = 40, // just in case (there are currently 27 widgets)
				len = widgets.length,
				deps = defaults.dependencies.widgets;
			for ( dep in deps ) {
				if ( typeof dep === 'string' ) {
					for ( indx = 0; indx < len; indx++ ) {
						// make sure indexOf is not matching 'column' instead of 'columnSelector' with surrounding spaces
						if ( ( ' ' + dep + ' ' ).indexOf( ' ' + widgets[indx] + ' ' ) >= 0 && widgets.indexOf( deps[ dep ] ) < 0 ) {
							widgets.push( deps[ dep ] );
							// keep checking newly added widgets, in case 'filter' is added and 'storage' hasn't been
							if ( len < maxDeps ) { len++; }
							continue;
						}
					}
				}
			}
			return widgets;
		},
		formFileNames = function() {
			var wrap = defaults.wrappers;
			// add widget path & file extension
			pkg.processedWidgets = ( wrap.widgetPrefix +
				pkg.selectedWidgets.join( wrap.widgetSuffix + ',' + wrap.widgetPrefix ) +
				wrap.widgetSuffix ).split( ',' );
			console.info( 'Creating a widgets file with: ' + pkg.selectedWidgets.join(', ') );

			if ( pkg.selectedParsers ) {
				// parser path & file extension
				pkg.processedParsers = ( wrap.parserPrefix +
					pkg.selectedParsers.join( wrap.parserSuffix + ',' + wrap.parserPrefix ) +
					wrap.parserSuffix ).split( ',' );
				console.info( 'Creating a combined file with selected widgets & these parsers: ' + pkg.selectedParsers.join(', ') );
				// make this look nice for the banner
				pkg.selectedParsers = ') & parsers ( ' + pkg.selectedParsers + ' ';
			}
			console.info( pkg.processedParsers );
		};

	// minified banner template - updated ##/##/20## (v2.##.##)
	pkg.banner = '/*! <%= pkg.name %> (FORK) - updated ' +
		'<%= grunt.template.today("yyyy-mm-dd") %> (v<%= pkg.version %>)*/\n';

	pkg.buildWidget = defaults.standardWidgetFileName;
	pkg.buildCombined = 'jquery.tablesorter.combined.js';
	pkg.processedParsers = '!js/parsers/*.js';

	// Project configuration.
	grunt.initConfig({
		pkg: pkg,

		clean: {
			build: {
				src: [ 'dist/**/**/**/*', 'dist/**/**/*', 'dist/**/*', 'dist' ]
			},
			css: {
				src: [ 'dist/css/*.css', '!dist/css/*.min.css', '!dist/css/theme.blue.css' ]
			}
		},

		copy: {
			css: {
				files : [{
					expand: true,
					dot: true,
					flatten: true,
					src: [ 'css/*.css', 'addons/pager/*.css' ],
					dest: 'dist/css/',
					rename: function( dest, src ) {
						if ( /black-ice/.test( src ) ) {
							src = src.replace( /-/, '' );
						}
						return dest + src;
					}
				}]
			},
			less: {
				expand: true,
				flatten: true,
				src:  'css/*.less',
				dest: 'dist/css/less/'
			},
			scss: {
				expand: true,
				flatten: true,
				src:  'css/*.scss',
				dest: 'dist/css/scss/'
			},
			images: {
				expand: true,
				flatten: true,
				src:  [ 'addons/pager/icons/*', 'css/images/*' ],
				dest: 'dist/css/images/'
			}
		},

		concat: {
			main: {
				options: {
					banner: defaults.wrapperUMD.coreBanner,
					footer: defaults.wrapperUMD.footer
				},
				src: [ 'js/jquery.tablesorter.js' ],
				dest: 'dist/js/jquery.tablesorter.js'
			},
			widgets: {
				options: {
					banner: defaults.wrapperUMD.banner,
					footer: defaults.wrapperUMD.footer
				},
				src: [
					'<%= pkg.processedWidgets %>',
					'!js/widgets/_test-*.js',
					'!js/widgets/*.min.js'
				],
				dest: 'dist/js/<%= pkg.buildWidget %>'
			},
			everything: {
				options: {
					banner: defaults.wrapperUMD.banner,
					footer: defaults.wrapperUMD.footer
				},
				src: [
					'js/jquery.tablesorter.js',
					'<%= pkg.processedWidgets %>',
					'<%= pkg.processedParsers %>',
					'!js/widgets/_test-*.js',
					'!js/widgets/*.min.js'
				],
				dest: 'dist/js/<%= pkg.buildCombined %>'
			},
			// keep all the existing jsFiddle demos from breaking
			copybackWidgets: {
				options: {
					banner: defaults.noModBanner
				},
				src : ['dist/js/<%= pkg.buildWidget %>'],
				dest: 'js/<%= pkg.buildWidget %>'
			},
			copybackCombined: {
				options: {
					banner: defaults.noModBanner
				},
				src : ['dist/js/<%= pkg.buildCombined %>'],
				dest: 'js/<%= pkg.buildCombined %>'
			}
		},

		jscs: {
			src: [
				'addons/pager/*.js',
				'!addons/pager/*.min.js',
				'js/jquery.*.js',
				'js/**/*.js',
				'!js/_test-*.js',
				'!js/jquery.tablesorter.combined.js',
				'!js/jquery.tablesorter.widgets.js',
				'!js/extras/jquery.dragtable.mod.js', // indents with spaces; keeping original formatting to make diffs easier
				'!js/extras/jquery.metadata.js', // phasing this one out anyway
				'!js/**/_test-*.js',
				'!js/*.min.js',
				'!js/**/semver*.js'
			],
			options: {
				config: '.jscsrc'
			}
		},

		jshint: {
			files: {
				src: [
					'addons/pager/*.js',
					'!addons/pager/*.min.js',
					'js/jquery.*.js',
					'js/**/*.js',
					'!js/_test-*.js',
					'!js/**/_test-*.js',
					'!js/*.min.js',
					'!js/**/semver*.js'
				]
			},
			options: {
				globals: {
					'JSON': false,
					'localStorage': false,
					'navigator': false,
					'console': false,
					'require': false,
					'define': false,
					'module': false
				},
				'loopfunc': true,
				'jquery': true,
				'browser': true,
				'es3': true,
				'unused': true,
				'undef': true
			}
		},

		htmlhint: {
			html1: {
				options: {
					'tag-pair': true
				},
				src: ['docs/*.html', 'beta-testing/*.html']
			}
		},

		uglify: {
			options: {
				output: {
					comments: /^!/,
					beautify: false,
					ie8: true
				},
				report: 'gzip'
			},
			allFiles: {
				options: {
					banner: defaults.wrapperUMD.coreBanner,
					footer: defaults.wrapperUMD.footerMain
				},
				files: [{
					expand: true,
					cwd: './js/', // Src matches are relative to this path.
					src: [
						'**/*.js',
						'!jquery.tablesorter.js',
						'!_test-*.js',
						'!**/_test-*.js',
						'!*.min.js',
						'!**/semver*.js',
					],
					dest: 'dist/js/',
					ext: '.min.js', // Dist files will have this extension.
					extDot: 'last'  // Extensions in filenames begin after this dot
				}]
			},
			// jquery.tablesorter.min.js is being minified from js/jquery.tablesorter.js
			// so it doesn't include the UMD wrapper! Point to the dist/js/ version
			main: {
				files : [{
					expand: true,
					cwd: './dist/js/',
					src: [ 'jquery.tablesorter.js' ],
					dest: 'dist/js/',
					ext: '.min.js', // Dist files will have this extension.
					extDot: 'last'  // Extensions in filenames begin after this dot
				}]
			},
			pageraddon: {
				options: {
					banner: defaults.wrapperUMD.coreBanner,
					footer: defaults.wrapperUMD.footerMain
				},
				files: {
					'dist/js/extras/jquery.tablesorter.pager.min.js': [ 'addons/pager/*.js' ]
				}
			},
			// Semver doesn't need a wrapper
			semver: {
				files: [{
					expand: true,
					cwd: './js/', // Src matches are relative to this path.
					src: [
						'**/semver-mod.js',
					],
					dest: 'dist/js/',
					ext: '.min.js', // Dist files will have this extension.
					extDot: 'last'  // Extensions in filenames begin after this dot
				}]
			}
		},

		cssmin: {
			target: {
				files: [{
					expand: true,
					flatten: true,
					cwd: 'dist/css',
					src: ['*.css', '!_test-*.css'],
					dest: 'dist/css',
					ext: '.min.css',
					extDot: 'last'
				}]
			}
		},

		qunit: {
			files: [ 'test.html' ]
		},

		watch: {
			scripts: {
				files: [
					'js/*.js',
					'js/**/*.js',
					'!js/_test-*.js',
					'!js/*.min.js'
				],
				tasks: [ 'build' ]
			}
		}

	});

	grunt.loadNpmTasks( 'grunt-contrib-clean' );
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-htmlhint' );
	grunt.loadNpmTasks( 'grunt-contrib-qunit' );
	grunt.loadNpmTasks( 'grunt-contrib-concat' );
	grunt.loadNpmTasks( 'grunt-contrib-uglify' );
	grunt.loadNpmTasks( 'grunt-contrib-copy' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-contrib-cssmin' );
	grunt.loadNpmTasks( 'grunt-jscs' );

	grunt.registerTask( 'test', [ 'jscs', 'jshint', 'htmlhint', 'qunit' ] );

	tasks = [
		'clean:build',
		'copy',
		'concat',
		'uglify',
		'cssmin',
		'clean:css',
		'updateManifest'
	];

	// basic = same as before: core, widgets, filterformatter all separate
	grunt.registerTask( 'default', 'Default build', function() {
		pkg.selectedWidgets = addWidgetDependencies( defaults.standardWidgets );
		formFileNames();
		grunt.task.run(tasks);
	});

	// quick build to just minify files for pushes between updates
	grunt.registerTask( 'quick', [ 'copy', 'uglify', 'clean:css' ] );

	// enter 'grunt custom:{filename}' (not including the '.json')
	// to load in a custom json file
	// the expected JSON format is (with custom widgets in a string):
	// { "widgets" : "columnHighlight filter resizable saveSort stickyHeaders uitheme" }
	grunt.registerTask( 'custom', 'Custom build', function(file) {
		var temp, widgets,
			parsers = '',
			deps = true;

		/* Allow developer to set up a custom widget build (json file will have settings)*/
		try {
			temp = grunt.file.readJSON( file );
			if ( temp ) {
				parsers = temp.parsers;
				// include dependencies?
				deps = ('includeDependencies' in temp) ? temp.includeDependencies : true;
				// custom file name?
				pkg.buildWidget = ('destFileName' in temp) ? temp.destFileName : 'jquery.tablesorter.custom-widgets.js';
				// widgets to include
				widgets = (temp.widgets || '');
				if (widgets.replace(/\s+/g,'') === '') {
					grunt.log.error('No widgets found in custom build file.');
					console.info('Continuing build with default widgets...');
					temp = defaults.standardWidgets;
					pkg.buildWidget = defaults.standardWidgetFileName;
				} else {
					temp = widgets.split(/\s+/);
				}
			}
		} catch (err) {
			grunt.log.error('Custom build json not found - Use "grunt custom:{filename}"');
			console.info('Continuing build with default settings');
			temp = defaults.standardWidgets;
		}
		// add dependencies
		pkg.selectedWidgets = deps ? addWidgetDependencies( temp ) : temp;
		pkg.selectedParsers = parsers.split( /\s+/ );
		formFileNames();
		grunt.task.run(tasks);
	});

	function escapeRegExp(str) {
		return str.replace(/[$()*+\-.\/?[\\\]^{|}]/g, '\\$&');
	}

	// update tablesorter.jquery.json file version numbers to match the package.json version
	grunt.registerTask( 'updateManifest', function() {
		var i, project,
			projectFile = [ 'tablesorter.jquery.json' ],
			len = projectFile.length;
		for ( i = 0; i < len; i++ ) {
			if ( !grunt.file.exists( projectFile[ i ] ) ) {
				grunt.log.error( 'file ' + projectFile[ i ] + ' not found' );
				return true; // return false to abort the execution
			}
			project = grunt.file.readJSON( projectFile[ i ] ); // get file as json object
			project.version = pkg.version;
			grunt.file.write( projectFile[i], JSON.stringify( project, null, 2 ) ); // serialize it back to file
		}
		// check internal version number
		project = grunt.file.read('js/jquery.tablesorter.js');
		if (
			new RegExp(escapeRegExp('/*! TableSorter (FORK) v' + pkg.version)).test(project) &&
			new RegExp(escapeRegExp("version : '" + pkg.version)).test(project)
		) {
			console.info('versions all match!');
		} else {
			grunt.log.writeln('\n**** version mismatch! ****'['red'].bold);
		}
	});

};
