var fs = require('fs');

module.exports = function(grunt) {
    grunt.initConfig({

        less: {
            build: {
                options: {},
                files: {
                    'dist/waves.css': 'src/less/waves.less'
                }
            },
            minified: {
                options: {
                    cleancss:true
                },
                files: {
                    'dist/waves.min.css': 'src/less/waves.less'
                }
            },
            // re-minify everything in tests/ so that they all
            // have the same minification for comparision
            test: {
                options: {
                    cleancss:true,
                    cleancssOptions: {
                        keepSpecialComments:'0'
                    }
                },
                files: {
                    'tests/less/waves.min.css': 'src/less/waves.less',
                    'tests/sass/waves.min.css': 'tests/sass/waves.css',
                    'tests/scss/waves.min.css': 'tests/scss/waves.css',
                    'tests/stylus/waves.min.css': 'tests/stylus/waves.css'
                }
            }
        },
        
        jshint: {
            
            files: [
                'gruntfile.js', 
                'src/js/*.js',
            ],
            
            options: {
                globals: {
                    console: true
                }
            }
        },
   
        uglify: {
            options: {
                mangle: true,  // false when debugging
                compress: true,  // false when debugging
                sourceMap: true,
                sourceMapName: 'dist/waves.min.js.map',
                preserveComments: 'some'
            },
            js: {
                files: {
                    'dist/waves.min.js': ['src/js/waves.js']
                }
            }
        },

        // Copy to dist
        copy: {
			js: {
                expand: true,
                cwd: 'src/js',
				src: 'waves.js',
				dest: 'dist/'
			},
            docs: {
                expand: true,
                cwd: 'dist',
                src: ['waves.min.css', 'waves.min.js'],
                dest: 'docs/static'
            }
		},
        
        //convert less to stylus
        execute: {
            less2stylus: {
                call: function(grunt, options, async) {
                    var done = async();
                    var exec = require('child_process').exec;
                    exec('cd node_modules/less2stylus && ./less2stylus ../../src/less/waves.less', function (error, stdout, stderr) {
                        grunt.log.writeln('Executing less2styus...');

                        if (error) {
                            grunt.log.writeln('Error! ' + error);
                        }

                        var fs = require('fs');
                        fs.writeFile("src/stylus/waves.styl", stdout, function(err) {
                            if(err) {
                                grunt.log.writeln(err);
                            } else {
                                grunt.log.writeln("Stylus file was saved!");
                            }

                            done(); // let grunt resume
                        });
                    });
                }
            },

            less2scss: {
                //FUTURE: Put less2scss as it's own script
                call: function(grunt, options, async) {
                    var done = async();
                    var text = fs.readFileSync('src/less/waves.less', {encoding:'utf8'});

                    //replace @ with $
                    text = text.replace(/@(?!import|media|keyframes|-)/g, '$');
                    //replace mixins
                    text = text.replace(/\.([\w\-]*)\s*\((.*)\)\s*\{/g, '@mixin $1($2){');
                    //replace includes
                    text = text.replace(/\.([\w\-]*\(.*\)\s*;)/g, '@include $1');
                    //replace string literals
                    //eg. ~'!important' -> #{"!important"}
                    text = text.replace(/~(?:\"|\')(.*)(?:\"|\')/g, '#{"$1"}');

                    //NOTE: for true less->scss transpiling we'd need to replace spin to adjust-hue (not needed but anyway)

                    fs.writeFileSync('src/scss/waves.scss', text);

                    done();
                }
            },
            
            test: {
                call: function(grunt, options, async) {
                    var done = async();
                    var lessTest = fs.readFileSync('tests/less/waves.min.css', {encoding:'utf8'});
                    var sassTest = fs.readFileSync('tests/sass/waves.min.css', {encoding:'utf8'});
                    var scssTest = fs.readFileSync('tests/scss/waves.min.css', {encoding:'utf8'});
                    var stylusTest = fs.readFileSync('tests/stylus/waves.min.css', {encoding:'utf8'});
                    
                    var failure = false;
                    if (lessTest != sassTest) {
                        grunt.log.writeln('ERROR: sass failed test.');
                        failure = true;
                    }
                    
                    if (lessTest != scssTest) {
                        grunt.log.writeln('ERROR: scss failed test.');
                        failure = true;
                    }
                    
                    if (lessTest != stylusTest) {
                        grunt.log.writeln('ERROR: stylus failed test.');
                        failure = true;
                    }
                    
                    if (sassTest != scssTest) {
                        grunt.log.writeln('WARNING: sass files aren\'t equal?');
                        failure = true;
                    }
                    
                    if (!failure) grunt.log.writeln('PASS: conversions generated same CSS');
                    
                    done();
                }
            }
        },

        'sass-convert': {
            options: {
                from: 'scss',
                to: 'sass',
                indent: 2
            },
            files: {
                cwd: 'src/scss',
                src: '*.scss',
                dest: 'src/sass'
            }
        },
        
        sass: {
            test: {
                files: [{
                    expand: true,
                    cwd: 'src',
                    src: ['**/*.sass', '**/*.scss'],
                    dest: 'tests/',
                    ext: '.css'
                }]
            }
        },
        
        stylus: {
            test: {
                files: {
                    'tests/stylus/waves.css': 'src/stylus/waves.styl'
                }
            }
        },
        
        clean: {
            test: ['tests/*']
        },

        watch: {
            script: {
               options: {
                    spawn: false,
                    event: ['added', 'deleted', 'changed']
                },
                files: ['src/**/*.js', 'src/**/*.less'],
                tasks: ['build']
            },
            grunt: {
                files: ['Gruntfile.js']
            }
        }
    });
    
    // Load module
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-contrib-stylus');
    grunt.loadNpmTasks('grunt-execute');
    grunt.loadNpmTasks('grunt-sass-convert');
    grunt.loadNpmTasks('grunt-contrib-clean');
    
    // Create grunt task
    grunt.registerTask('build', [
        'less:build', 
        'less:minified', 
        'jshint', 
        'uglify', 
        'copy', 
        'execute:less2stylus', 
        'execute:less2scss', 
        'sass-convert', 
        'sass:test', 
        'stylus:test', 
        'less:test', 
        'execute:test',
        'clean:test'
    ]);
    
    grunt.registerTask('default', ['build', 'watch']);
};
