// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const gulp = require('gulp');
const through = require('through');
const bufferFrom = require('buffer-from');
const concat = require('gulp-concat');
const pathLib = require('path');
const fs = require('fs');

/**
 * Task to combine scss into a single file.
 */
class CombineScssTask {

    /**
     * Finds the file and returns its content.
     *
     * @param capture Import file path.
     * @param baseDir Directory where the file was found.
     * @param paths Alternative paths where to find the imports.
     * @param parsedFiles Already parsed files to reduce size of the result.
     * @return Partially combined scss.
     */
    getReplace(capture, baseDir, paths, parsedFiles) {
        let parse = pathLib.parse(pathLib.resolve(baseDir, capture + '.scss'));
        let file = parse.dir + '/' + parse.name;

        if (file.slice(-3) === '.wp') {
            console.log('Windows Phone not supported "' + capture);
            // File was already parsed, leave the import commented.
            return '// @import "' + capture + '";';
        }

        if (!fs.existsSync(file + '.scss')) {
            // File not found, might be a partial file.
            file = parse.dir + '/_' + parse.name;
        }

        // If file still not found, try to find the file in the alternative paths.
        let x = 0;
        while (!fs.existsSync(file + '.scss') && paths.length > x) {
            parse = pathLib.parse(pathLib.resolve(paths[x], capture + '.scss'));
            file = parse.dir + '/' + parse.name;

            x++;
        }

        file = file + '.scss';

        if (!fs.existsSync(file)) {
            // File not found. Leave the import there.
            console.log('File "' + capture + '" not found');
            return '@import "' + capture + '";';
        }

        if (parsedFiles.indexOf(file) >= 0) {
            console.log('File "' + capture + '" already parsed');
            // File was already parsed, leave the import commented.
            return '// @import "' + capture + '";';
        }

        parsedFiles.push(file);
        const text = fs.readFileSync(file);

        // Recursive call.
        return this.scssCombine(text, parse.dir, paths, parsedFiles);
    }

    /**
     * Run the task.
     *
     * @param done Function to call when done.
     */
    run(done) {
        const paths = [
            'node_modules/ionic-angular/themes/',
            'node_modules/font-awesome/scss/',
            'node_modules/ionicons/dist/scss/'
        ];
        const parsedFiles = [];
        const self = this;

        gulp.src([
                './src/theme/variables.scss',
                './node_modules/ionic-angular/themes/ionic.globals.*.scss',
                './node_modules/ionic-angular/themes/ionic.components.scss',
                './src/**/*.scss',
            ]).pipe(through(function(file) { // Combine them based on @import and save it to stream.
                if (file.isNull()) {
                    return;
                }

                parsedFiles.push(file);
                file.contents = bufferFrom(self.scssCombine(
                        file.contents, pathLib.dirname(file.path), paths, parsedFiles));

                this.emit('data', file);
            })).pipe(concat('combined.scss')) // Concat the stream output in single file.
            .pipe(gulp.dest('.')) // Save file to destination.
            .on('end', done);
    }

    /**
     * Combine scss files with its imports
     *
     * @param content Scss string to treat.
     * @param baseDir Directory where the file was found.
     * @param paths Alternative paths where to find the imports.
     * @param parsedFiles Already parsed files to reduce size of the result.
     * @return Scss string with the replaces done.
     */
    scssCombine(content, baseDir, paths, parsedFiles) {
        // Content is a Buffer, convert to string.
        if (typeof content != "string") {
            content = content.toString();
        }

        // Search of single imports.
        let regex = /@import[ ]*['"](.*)['"][ ]*;/g;

        if (regex.test(content)) {
            return content.replace(regex, (m, capture) => {
                if (capture == "bmma") {
                    return m;
                }

                return this.getReplace(capture, baseDir, paths, parsedFiles);
            });
        }

        // Search of multiple imports.
        regex = /@import(?:[ \n]+['"](.*)['"][,]?[ \n]*)+;/gm;
        if (regex.test(content)) {
            return content.replace(regex, (m, capture) => {
                let text = '';

                // Divide the import into multiple files.
                const captures = m.match(/['"]([^'"]*)['"]/g);

                for (let x in captures) {
                    text += this.getReplace(captures[x].replace(/['"]+/g, ''), baseDir, paths, parsedFiles) + '\n';
                }

                return text;
            });
        }

        return content;
    }
}

module.exports = CombineScssTask;