const gulp = require('gulp');
const cssnano = require('cssnano')
const gulpLoadPlugins = require('gulp-load-plugins')
const terser = require('terser');
const del = require('del');
const browserify = require('browserify');
const tsify = require('tsify')
const babelify = require('babelify');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const merge = require('merge-stream');
const fs = require('fs');
const factor = require('factor-bundle');
const { debug } = require('console');

const $ = gulpLoadPlugins()

const sass = require('gulp-sass')(require('sass'));

/* ============================================================================
Base consts
============================================================================ */

// Project sources
const src = {
    root: 'src',
    manifests: 'src/manifests',
    styles: 'src/stylesheets',
    scripts: 'src/javascript',
    images: 'src/images',
    html: 'src/html',
}

// Build path
const tmp = {
    root: '.tmp',
    manifests: '.tmp/manifests',
    styles: '.tmp/stylesheets',
    scripts: '.tmp/javascript',
    images: '.tmp/images',
    html: '.tmp/html',
}

// Dist path
const dist = {
    root: 'dist',
    chrome: {
        root: 'dist/chrome',
        styles: 'dist/chrome/stylesheets',
        scripts: 'dist/chrome/javascript',
        images: 'dist/chrome/images',
        html: 'dist/chrome/html',
    },
    firefox: {
        root: 'dist/firefox',
        styles: 'dist/firefox/stylesheets',
        scripts: 'dist/firefox/javascript',
        images: 'dist/firefox/images',
        html: 'dist/firefox/html',
    },
    opera: {
        root: 'dist/opera',
        styles: 'dist/opera/stylesheets',
        scripts: 'dist/opera/javascript',
        images: 'dist/opera/images',
        html: 'dist/opera/html',
    },
    zip: 'dist/zips',
}

// Build mode
const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;

/* ============================================================================
Build tasks
============================================================================ */

gulp.task('styles', () => {
    return gulp.src(`${src.styles}/*.scss`)
        .pipe($.plumber())
        // sourcemap initialization
        .pipe($.if(isDev, $.sourcemaps.init()))
        // sass compilation
        .pipe(sass.sync({
            outputStyle: 'expanded',
            precision: 7,
            includePaths: ['.'],
        }).on('error', sass.logError))
        // autoprefix
        .pipe($.autoprefixer())
        // out stream size
        .pipe($.size({
            showFiles: true
        }))
        .pipe(gulp.dest(tmp.styles))
        .pipe($.rename({ suffix: '.min' }))
        // minimize and optimize
        .pipe($.if('*.css', $.postcss([
            cssnano({
                safe: true,
                autoprefixer: false,
                zindex: false,
                reduceIdents: {
                    keyframes: true,
                },
            })
        ])))
        // out stream size
        .pipe($.size({
            showFiles: true
        }))
        // write sourcemaps
        .pipe($.if(isDev, $.sourcemaps.write()))
        .pipe(gulp.dest(tmp.styles))
})

gulp.task('scripts', () => {
    const modules = [
        'app',
        'settings',
    ];

    const inputs = [];
    const streams = [];

    modules.forEach(module => {
        inputs.push(`${src.scripts}/${module}.ts`);
        streams.push(source(`${module}.js`));
    });

    const b = browserify(inputs, { debug: isDev });

    let outstream = b
        .plugin(tsify)
        .transform('babelify')
        .plugin(factor, { outputs: streams })
        .bundle()
        .pipe(source('common.js'))

    streams.forEach(stream => {
        outstream = outstream.pipe($.merge(stream));
    });

    return outstream
        .pipe($.plumber())
        .pipe(buffer())
        .pipe($.if(isDev, $.sourcemaps.init({ loadMaps: true })))
        .pipe($.terser({ compress: { drop_console: isProd, drop_debugger: isProd } }))
        .pipe($.rename({ suffix: '.min' }))
        .pipe($.size({
            showFiles: true,
        }))
        .pipe($.if(isDev, $.sourcemaps.write()))
        .pipe(gulp.dest(`${tmp.scripts}`));
})

gulp.task('images', () => {
    return gulp.src(`${src.images}/**/*`)
        .pipe($.plumber())
        .pipe($.imagemin([
            $.imagemin.gifsicle({ interlaced: true }),
            $.imagemin.mozjpeg({ progressive: true }),
            $.imagemin.optipng(),
            $.imagemin.svgo({ plugins: [{ cleanupIDs: false }] })
        ]))
        .pipe($.size({
            showFiles: true,
        }))
        .pipe(gulp.dest(tmp.images))
})

gulp.task('html', () => {
    return gulp.src(`${src.html}/**/*`)
        .pipe($.plumber())
        // any steps for HTML processing?
        .pipe($.size({
            showFiles: true,
        }))
        .pipe(gulp.dest(tmp.html))
})

gulp.task('manifests', () => {
    const templateFile = `${src.manifests}/manifest.template.json`;

    let template = JSON.parse(fs.readFileSync(templateFile))

    return gulp.src(`${src.manifests}/**/!(*.template).json`)
        .pipe($.plumber())
        .pipe($.replace('$name', template.name))
        .pipe($.replace('$shortName', template.short_name))
        .pipe($.replace('$version', template.version))
        .pipe($.replace('$semanticVersion', template.version_name))
        .pipe($.replace('$description', template.description))
        .pipe($.replace('$author', template.author))
        .pipe($.replace('$developer', JSON.stringify(template.developer)))
        .pipe($.replace('$homepageURL', template.homepage_url))
        .pipe($.size({
            showFiles: true,
        }))
        .pipe(gulp.dest(tmp.manifests))
})

/* ============================================================================
Watchers
============================================================================ */

gulp.task('watch', (done) => {
    gulp.watch(`${src.styles}/**/*.scss`, gulp.series('clean:build', 'styles', 'dist:copy', 'dist:zip'))

    gulp.watch(`${src.scripts}/**/*.ts`, gulp.series('clean:build', 'scripts', 'dist:copy', 'dist:zip'))

    gulp.watch(`${src.images}/**/*`, gulp.series('clean:build', 'images', 'dist:copy', 'dist:zip'))

    gulp.watch(`${src.html}/**/*`, gulp.series('clean:build', 'html', 'dist:copy', 'dist:zip'))

    gulp.watch(`${src.manifests}/**/*.*`, gulp.series('clean:build', 'manifests', 'dist:copy', 'dist:zip'))

    done();
})

/* ============================================================================
Clean
============================================================================ */

gulp.task('clean:build', del.bind(null, [tmp.root]))

gulp.task('clean:dist', del.bind(null, [dist.root], { force: true })) // Das Force brauchen wir, da das Assets Verzeichnis außerhalb des Working Directories ist.

gulp.task('clean', gulp.series('clean:build', 'clean:dist'))

/* ============================================================================
BUILD CLEAN ALL
============================================================================ */

gulp.task('build', gulp.series('manifests', 'images', 'scripts', 'styles', 'html'));

gulp.task('build:clean', gulp.series('clean:build', 'manifests', 'images', 'scripts', 'styles'));

/* ============================================================================
DIST CLEAN ALL
============================================================================ */

gulp.task('dist:chrome', (done) => {
    return merge(
        // copy images
        gulp.src(`${tmp.images}/**/*`)
            .pipe(gulp.dest(dist.chrome.images)),

        // copy scripts
        gulp.src(`${tmp.scripts}/**/*.{min.js,min.js.gz}`)
            .pipe(gulp.dest(dist.chrome.scripts)),

        // copy styles
        gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
            .pipe(gulp.dest(dist.chrome.styles)),

        // copy html
        gulp.src(`${tmp.html}/*.html`)
            .pipe(gulp.dest(dist.chrome.html)),

        gulp.src(`${tmp.manifests}/chrome*`)
            .pipe($.rename('manifest.json'))
            .pipe(gulp.dest(dist.chrome.root))
    );
})

gulp.task('dist:firefox', (done) => {
    return merge(
        // copy images
        gulp.src(`${tmp.images}/**/*`)
            .pipe(gulp.dest(dist.firefox.images)),

        // copy scripts
        gulp.src(`${tmp.scripts}/**/*.{min.js,min.js.gz}`)
            .pipe(gulp.dest(dist.firefox.scripts)),

        // copy styles
        gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
            .pipe(gulp.dest(dist.firefox.styles)),

        // copy html
        gulp.src(`${tmp.html}/*.html`)
            .pipe(gulp.dest(dist.firefox.html)),

        gulp.src(`${tmp.manifests}/firefox*`)
            .pipe($.rename('manifest.json'))
            .pipe(gulp.dest(dist.firefox.root))
    );
})

gulp.task('dist:opera', (done) => {
    return merge(
        // copy images
        gulp.src(`${tmp.images}/**/*`)
            .pipe(gulp.dest(dist.opera.images)),

        // copy scripts
        gulp.src(`${tmp.scripts}/**/*.{min.js,min.js.gz}`)
            .pipe(gulp.dest(dist.opera.scripts)),

        // copy styles
        gulp.src(`${tmp.styles}/*.{min.css,min.css.gz}`)
            .pipe(gulp.dest(dist.opera.styles)),

        // copy html
        gulp.src(`${tmp.html}/*.html`)
            .pipe(gulp.dest(dist.opera.html)),

        gulp.src(`${tmp.manifests}/opera*`)
            .pipe($.rename('manifest.json'))
            .pipe(gulp.dest(dist.opera.root))
    );
})

gulp.task('dist:copy', gulp.series('dist:chrome', 'dist:firefox', 'dist:opera'));

gulp.task('dist:zip', (done) => {
    gulp.src(`${dist.chrome.root}/**/*`)
        .pipe($.zip('chrome.zip'))
        .pipe(gulp.dest(dist.root));

    gulp.src(`${dist.firefox.root}/**/*`)
        .pipe($.zip('firefox.zip'))
        .pipe(gulp.dest(dist.root));

    gulp.src(`${dist.opera.root}/**/*`)
        .pipe($.zip('opera.zip'))
        .pipe(gulp.dest(dist.root));

    done();
})

gulp.task('dist', gulp.series('clean', 'build', 'dist:copy', 'dist:zip'));
