"use strict";

// Gulp and plugins
var
    gulp = require("gulp"),
    runSequence = require('run-sequence'),
    gulpUglify = require("gulp-uglify"),
    gulpRename = require("gulp-rename"),
    gulpReplace = require("gulp-replace-task"),
    gulpConcat = require("gulp-concat"),
    shell = require('gulp-shell'),
    webserver = require('gulp-webserver'),
    rev = require("gulp-rev"),
    revReplace = require("gulp-rev-replace"),
    revDel = require("rev-del"),
    through2 = require("through2"),
    del = require("del"),
    proxy = require('http-proxy-middleware'),
    config = require('./gulp.config');

/** helper functions, variables and config */
var pkg = require('./package.json');
var app = config.defaultEnvironment;
var appConfig = config[app];

/**
 * copying assets to selected dist folder
 */
function copyAssets() {
    var dest = appConfig.dist;

    return Promise.all([
        new Promise((resolve, reject) => {
            gulp.src([
                    "jspm_packages/**/*.@(png|gif|eot|svg|ttf|woff|woff2)",
                    // exclude is not supported thus separate glob
                    // these matching directories contain a lot of noise - do not copy it
                    "!jspm_packages/**/@(docs|gabelerner|example?)/**"
                ])
                .pipe(gulp.dest(dest + "/jspm_packages"))
                .on("finish", resolve);
        }),
        new Promise((resolve, reject) => {
            gulp.src("src/workers/TableDataWorker.js")
                .pipe(gulpUglify())
                .pipe(gulp.dest(dest + "/src/workers"))
                .on("finish", resolve);
        }),
        new Promise((resolve, reject) => {
            // worker dependencies
            gulp.src("jspm_packages/npm/underscore*/underscore.js")
                .pipe(gulpRename("underscore.js"))
                .pipe(gulpUglify())
                .pipe(gulp.dest(dest + "/src/workers/lib"))
                .on("finish", resolve);
        }),
        new Promise((resolve, reject) => {
            // config files
            gulp.src("src/config-" + app + ".js")
                .pipe(gulpRename("config.js"))
                .pipe(gulpReplace({patterns: [{ match: "version", replacement: pkg.version }]}))
                .pipe(gulp.dest(dest))
                .on("finish", resolve);
        })
    ]);

}

/**
 * processing css files
 * function processes less generated files and generates
 * bundle.css
 */
function processCSSFiles() {
    var dest = appConfig.dist;

    var p1 = new Promise((resolve, reject) => {
        gulp.src(dest+"/bundle-from-less.css")
            .pipe(through2.obj(function(chunk, enc, cb) {
                // a no-op that confirms there is css from LESS so I can safely delete bundle.css
                // Delete CSS generated by plugin-css in jspm bundle-sfx as it's not used
                del.sync([dest+"/bundle.css", dest+"/bundle.css.map"]);
                cb(null, chunk);
            }))

            // fix sourceMappingURL
            .pipe(gulpReplace({patterns: [{
                match: /sourceMappingURL=bundle-from-less\.css\.map/,
                replacement: "sourceMappingURL=bundle.css.map"
            }]}))
            .pipe(gulpRename("bundle.css"))
            .pipe(gulp.dest("./"+dest))
            .pipe(through2.obj(function(chunk, enc, cb) {
                // Delete original files
                del.sync([dest+"/bundle-from-less.css"]);
                cb();
            }))
            .on("finish", resolve);
    });

    var p2 = new Promise((resolve, reject) => {
        // Rename CSS sourceMap file
        gulp.src(dest+"/bundle-from-less.css.map")
            .pipe(gulpRename("bundle.css.map"))
            .pipe(gulp.dest("./" + dest))
            .pipe(through2.obj(function(chunk, enc, cb) {
                del.sync(dest+"/bundle-from-less.css.map");
                cb();
            }))
            .on("finish", resolve);
    });

    return Promise.all([p1, p2]);
}

// switch app to ep
gulp.task("ep", function() {
    app = 'ep';
    appConfig = config[app];
});

// switch app to aps
gulp.task('aps', function() {
    app = 'aps';
    appConfig = config[app];
});

// switch app to apss
gulp.task('apss', function() {
    app = 'apss';
    appConfig = config[app];
});

gulp.task('prependSystemjsPolyfills', function() {
    var dest = appConfig.dist;
    var file = appConfig.bundleJS;

    return gulp.src([
            "jspm_packages/system-polyfills.js",
            dest + "/" + file
        ])
        .pipe(gulpConcat(file))
        .pipe(gulp.dest(dest));
});

gulp.task("adjustSystemjsConfigForTest", function() {
    gulp.src("system.config.js")
        .pipe(gulpReplace({
            patterns: [{
                match: /baseURL: "[^"]+"/,
                replacement: 'baseURL: "../.."'
            }]
        }))
        .pipe(gulp.dest("test/Unit"));
});

gulp.task("copyAssets", function() {
    let promises = [];
    promises = promises.concat(copyAssets());
    promises = promises.concat(processCSSFiles());

    promises.push(new Promise((resolve, reject) => {
        gulp.src(appConfig.initHTML)
            .pipe(gulpReplace({ patterns: [{ match: "version", replacement: pkg.version }]}))
            .pipe(gulp.dest(appConfig.dist))
            .on("finish", resolve);
    }));

    // entry file
    if (app == 'apss') {
        promises.push(new Promise((resolve, reject) => {
            gulp.src("sample-data/*")
                .pipe(gulp.dest("./dist-" + app + "/sample-data"))
                .on("finish", resolve);
        }));
        promises.push(new Promise((resolve, reject) => {
            gulp.src("./analysis-page-server-static-embed-example.html")
                .pipe(gulpReplace({patterns: [{match: "version", replacement: pkg.version}]}))
                .pipe(gulp.dest("./dist-apss"))
                .on("finish", resolve);
        }));
    }

    return Promise.all(promises);
});

gulp.task('jspmBundleSfx', function() {
    var cmd = './node_modules/.bin/jspm bundle-sfx ' + appConfig.initJS + ' ' + appConfig.dist + '/' + appConfig.bundleJS + ' --minify';
    return gulp
        .src('node_modules/.bin/jspm')
        .pipe(shell(cmd, { quiet: true }));
});
/**
 * It's intended to be called when all assets are generated and copied to
 * proper dist location.
 */
gulp.task("revAssets", function() {
    return gulp.src([
        appConfig.dist + "/" + appConfig.bundleJS,
        appConfig.dist + "/bundle.css",
        appConfig.dist + "/config.js"
    ])
    .pipe(rev())
    .pipe(gulp.dest(appConfig.dist))
    .pipe(rev.manifest())
    .pipe(revDel({dest: appConfig.dist}))
    .pipe(gulp.dest(appConfig.dist));
});

gulp.task("revReplace", function() {
    var manifest = gulp.src(appConfig.dist + "/rev-manifest.json");
    return gulp.src([
        appConfig.dist + "/" + appConfig.initHTML
    ])
    .pipe(revReplace({manifest}))
    .pipe(gulp.dest(appConfig.dist));
});

gulp.task("build", function(callback) {
    runSequence('jspmBundleSfx', "copyAssets", "prependSystemjsPolyfills", "revAssets", "revReplace", callback);
});

gulp.task('serve', function() {
    return gulp.src(appConfig.dist)
        .pipe(webserver({
            host: '0.0.0.0',
            port: config.port,
            livereload: false,
            directoryListing: { enable: true, path: appConfig.dist },
            open: false,
            middleware:  [
                proxy('/expressionplot/**', { target: config.apiTarget, changeOrigin: true })
            ]
        }));
});

gulp.task("default", function(callback) {
    return runSequence('ep', 'build', 'aps', 'build', 'apss', 'build', callback);
});
