// ==========================================================================
// Gulp build script
// ==========================================================================
/*global require, __dirname,Buffer*/
/*jshint -W079 */

var fs          = require("fs"),
    path        = require("path"),
    gulp        = require("gulp"),
    gutil       = require("gulp-util"),
    concat      = require("gulp-concat"),
    uglify      = require("gulp-uglify"),
    less        = require("gulp-less"),
    sass        = require("gulp-sass"),
    cleanCSS    = require("gulp-clean-css"),
    run         = require("run-sequence"),
    prefix      = require("gulp-autoprefixer"),
    svgstore    = require("gulp-svgstore"),
    svgmin      = require("gulp-svgmin"),
    rename      = require("gulp-rename"),
    s3          = require("gulp-s3"),
    replace     = require("gulp-replace"),
    open        = require("gulp-open"),
    size        = require("gulp-size"),
    through     = require("through2");

var root = __dirname,
paths = {
    plyr: {
        // Source paths
        src: {
            less:       path.join(root, "src/less/**/*"),
            scss:       path.join(root, "src/scss/**/*"),
            js:         path.join(root, "src/js/**/*"),
            sprite:     path.join(root, "src/sprite/*.svg")
        },
        // Output paths
        output:         path.join(root, "dist/")
    },
    demo: {
        // Source paths
        src: {
            less:       path.join(root, "demo/src/less/**/*"),
            js:         path.join(root, "demo/src/js/**/*"),
            sprite:     path.join(root, "demo/src/sprite/**/*")
        },
        // Output paths
        output:         path.join(root, "demo/dist/"),
        // Demo
        root:           path.join(root, "demo/")
    },
    upload: [path.join(root, "dist/**"), path.join(root, "demo/dist/**")]
},

// Task arrays
tasks = {
    less:   [],
    scss:   [],
    js:     [],
    sprite: []
},

// Fetch bundles from JSON
bundles = loadJSON(path.join(root, "bundles.json")),
package = loadJSON(path.join(root, "package.json"));

// Load json
function loadJSON(path) {
    try {
        return JSON.parse(fs.readFileSync(path));
    }
    catch(err) {
        return {};
    }
}

// Create a file from a string
// http://stackoverflow.com/questions/23230569/how-do-you-create-a-file-from-a-string-in-gulp
function createFile(filename, string) {
    var src = require('stream').Readable({ 
        objectMode: true 
    });
    src._read = function () {
        this.push(new gutil.File({ 
            cwd: "", 
            base: "", 
            path: filename, 
            contents: new Buffer(string),
            // stats also required for some functions
            // https://nodejs.org/api/fs.html#fs_class_fs_stats
            stat: {
                size: string.length
            } 
        }));
        this.push(null);
    }
    return src
}

var build = {
    js: function (files, bundle) {
        for (var key in files) {
            (function(key) {
                var name = "js-" + key;
                tasks.js.push(name);

                gulp.task(name, function () {
                    return gulp
                        .src(bundles[bundle].js[key])
                        .pipe(concat(key))
                        .pipe(uglify())
                        .pipe(gulp.dest(paths[bundle].output));
                });
            })(key);
        }
    },
    less: function(files, bundle) {
        for (var key in files) {
            (function (key) {
                var name = "less-" + key;
                tasks.less.push(name);

                gulp.task(name, function () {
                    return gulp
                        .src(bundles[bundle].less[key])
                        .pipe(less())
                        .on("error", gutil.log)
                        .pipe(concat(key))
                        .pipe(prefix(["last 2 versions"], { cascade: true }))
                        .pipe(cleanCSS())
                        .pipe(gulp.dest(paths[bundle].output));
                });
            })(key);
        }
    },
    scss: function(files, bundle) {
        for (var key in files) {
            (function (key) {
                var name = "scss-" + key;
                tasks.scss.push(name);

                gulp.task(name, function () {
                    return gulp
                        .src(bundles[bundle].scss[key])
                        .pipe(sass())
                        .on("error", gutil.log)
                        .pipe(concat(key))
                        .pipe(prefix(["last 2 versions"], { cascade: true }))
                        .pipe(cleanCSS())
                        .pipe(gulp.dest(paths[bundle].output));
                });
            })(key);
        }
    },
    sprite: function(bundle) {
        var name = "sprite-" + bundle;
        tasks.sprite.push(name);

        // Process Icons
        gulp.task(name, function () {
            return gulp
                .src(paths[bundle].src.sprite)
                .pipe(svgmin({
                    plugins: [{
                        removeDesc: true
                    }]
                }))
                .pipe(svgstore())
                .pipe(rename({ basename: bundle }))
                .pipe(gulp.dest(paths[bundle].output));
        });
    }
};

// Plyr core files
build.js(bundles.plyr.js, "plyr");
build.less(bundles.plyr.less, "plyr");
build.scss(bundles.plyr.scss, "plyr");
build.sprite("plyr");

// Demo files
build.less(bundles.demo.less, "demo");
build.js(bundles.demo.js, "demo");
build.sprite("demo");

// Build all JS
gulp.task("js", function(){
    run(tasks.js);
});

// Build SCSS (for testing, default is LESS)
gulp.task("scss", function(){
    run(tasks.scss);
});

// Watch for file changes
gulp.task("watch", function () {
    // Plyr core
    gulp.watch(paths.plyr.src.js, tasks.js);
    gulp.watch(paths.plyr.src.less, tasks.less);
    gulp.watch(paths.plyr.src.sprite, tasks.sprite);

    // Demo
    gulp.watch(paths.demo.src.js, tasks.js);
    gulp.watch(paths.demo.src.less, tasks.less);
    gulp.watch(paths.demo.src.sprite, tasks.sprite);
});

// Default gulp task
gulp.task("default", function(){
    run(tasks.js, tasks.less, tasks.sprite, "watch");
});

// Publish a version to CDN and demo
// --------------------------------------------

// Some options
var aws = loadJSON(path.join(root, "aws.json")),
version = package.version,
maxAge  = 31536000, // seconds 1 year
options = {
    cdn: {
        headers: {
            "Cache-Control": "max-age=" + maxAge,
            "Vary": "Accept-Encoding"
        }
    },
    demo: {
        headers: {
            "Cache-Control": "no-cache, no-store, must-revalidate, max-age=0",
            "Vary": "Accept-Encoding"
        }
    },
    symlinks: function(version, filename) {
        return {
            headers: {
                // http://stackoverflow.com/questions/2272835/amazon-s3-object-redirect
                "x-amz-website-redirect-location": "/" + version + "/" + filename,
                "Cache-Control": "no-cache, no-store, must-revalidate, max-age=0"
            }
        }
    }
};

// If aws is setup
if("cdn" in aws) {
    var regex       = "(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)(?:-[\\da-z\\-]+(?:\.[\\da-z\\-]+)*)?(?:\\+[\\da-z\\-]+(?:\.[\\da-z\\-]+)*)?",
    cdnpath         = new RegExp(aws.cdn.domain + "\/" + regex, "gi"),
    semver          = new RegExp("v" + regex, "gi"),
    localPath       = new RegExp("(\.\.\/)?dist", "gi"),
    versionPath     = "https://" + aws.cdn.domain + "/" + version;
}

// Publish version to CDN bucket
gulp.task("cdn", function () {
    console.log("Uploading " + version + " to " + aws.cdn.domain + "...");

    // Upload to CDN
    return gulp.src(paths.upload)
        .pipe(size({
            showFiles: true,
            gzip: true
        }))
        .pipe(rename(function (path) {
            path.dirname = path.dirname.replace(".", version);
        }))
        .pipe(replace(localPath, versionPath))
        .pipe(s3(aws.cdn, options.cdn));
});

// Publish to demo bucket
gulp.task("demo", function () {
    console.log("Uploading " + version + " demo to " + aws.demo.domain + "...");

    // Replace versioned files in readme.md
    gulp.src([root + "/readme.md"])
        .pipe(replace(cdnpath, aws.cdn.domain + "/" + version))
        .pipe(gulp.dest(root));

    // Replace versioned files in plyr.js
    gulp.src(path.join(root, "src/js/plyr.js"))
        .pipe(replace(semver, "v" + version))
        .pipe(replace(cdnpath, aws.cdn.domain + "/" + version))
        .pipe(gulp.dest(path.join(root, "src/js/")));

    // Replace local file paths with remote paths in demo HTML
    // e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
    gulp.src([paths.demo.root + "*.html"])
        .pipe(replace(localPath, versionPath))
        .pipe(s3(aws.demo, options.demo));

    // Upload error.html to cdn (as well as demo site)
    return gulp.src([paths.demo.root + "error.html"])
        .pipe(replace(localPath, versionPath))
        .pipe(s3(aws.cdn, options.demo));
});

// Open the demo site to check it's sweet
gulp.task("symlinks", function () {
    console.log("Updating symlinks...");

    return gulp.src(paths.upload)
        .pipe(through.obj(function (chunk, enc, callback) {
            if (chunk.stat.isFile()) {
                // Get the filename
                var filename = chunk.path.split("/").reverse()[0];

                // Create the 0 byte redirect files to upload
                createFile(filename, "")
                    .pipe(rename(function (path) {
                        path.dirname = path.dirname.replace(".", "latest");
                    }))
                    // Upload to S3 with correct headers
                    .pipe(s3(aws.cdn, options.symlinks(version, filename)));
            }

            callback(null, chunk);
        }));
});

// Open the demo site to check it's sweet
gulp.task("open", function () {
    console.log("Opening " + aws.demo.domain + "...");

    // A file must be specified or gulp will skip the task
    // Doesn't matter which file since we set the URL above
    // Weird, I know...
    return gulp.src([paths.demo.root + "index.html"])
        .pipe(open("", {
            url: "http://" + aws.demo.domain
        }));
});

// Do everything
gulp.task("publish", function () {
    run(tasks.js, tasks.less, tasks.sprite, "cdn", "demo", "symlinks");
});
