/**
 * watch file api
 */

'use strict';

var fs                = require('fs'),
    path              = require('path'),
    storage           = require('./storage'),
    compilersManager  = require('./compilersManager.js'),
    util              = require('./util'),
    $                 = global.jQuery,

    projectsDb        = storage.getProjects(),
    watchedCollection = {}, //watched file Collection
    importsCollection = {
        //src: [parentSrc,...]
    };  //imports Collection

/**
 * add file
 * @param {Object Array || single Object} fileInfo files info
 */
exports.add = function (fileInfo) {
    if (Array.isArray(fileInfo)) {
        fileInfo.forEach(function (item) {
            var pid = item.pid,
                src = item.src,
                file = projectsDb[pid].files[src];
            addWatchListener(src);
            watchedCollection[src] = $.extend({}, file);
        });
    } else {
        var pid = fileInfo.pid,
            src = fileInfo.src;
        addWatchListener(src);
        watchedCollection[src] = $.extend({}, projectsDb[pid].files[src]);
    }
};

/**
 * remove file
 * @param {String Array || single String} fileSrc file src
 */
exports.remove = function (fileSrc) {
    if (Array.isArray(fileSrc)) {
        fileSrc.forEach(function (item) {
            removeWatchListener(item);
            delete watchedCollection[item];
        });
    } else {
        removeWatchListener(fileSrc);
        delete watchedCollection[fileSrc]
    }
};

/**
 * update file
 * @param  {Object Array || single Object} file file object
 */
exports.update = function (fileInfo) {
    if (Array.isArray(fileInfo)) {
        fileInfo.forEach(function (item) {
            //更新
            var pid = item.pid,
                src = item.src,
                file = projectsDb[pid].files[src];
            watchedCollection[src] = $.extend({},watchedCollection[src],file);
        });
    } else {
        //更新
        var pid = fileInfo.pid,
            src = fileInfo.src,
            file = projectsDb[pid].files[src];
        watchedCollection[src] = $.extend({},watchedCollection[src],file);
    }
};

/**
 * change compile status
 * @param  {String}   pid            file project id
 * @param  {String}   fileSrc        file src
 * @param  {Boolean}  compileStatus  target status
 */
exports.changeCompile = function (pid, fileSrc, compileStatus) {
    if (compileStatus && !watchedCollection[fileSrc]) {
        addWatchListener(fileSrc);
        watchedCollection[fileSrc] = projectsDb[pid].files[fileSrc];
    }

    watchedCollection[fileSrc].compile = compileStatus;
};

/**
 * add imports file
 * @param {Array} files   imports
 * @param {Array} paths   import folder path
 * @param {String} srcFile import's src
 */
exports.addImports = function (imports, srcFile) {
    if (!Array.isArray(imports) || imports.length === 0) {
        return false;
    }

    // update imports of watched file
    var isWatchedFile = watchedCollection[srcFile];
    if (isWatchedFile && isWatchedFile.imports) {
        var oldImports = isWatchedFile.imports,
            importsString = imports.join(),
            invalidImports;

        //filter invalid file
        invalidImports = oldImports.filter(function (item) {
            return importsString.indexOf(item) === -1
        });

        invalidImports.forEach(function (item) {
            importsCollection[item] = importsCollection[item].filter(function (element) {
                return element !== srcFile;
            });
        });

        watchedCollection[srcFile].imports = imports;

        //save import data of project
        var pid = watchedCollection[srcFile].pid;
        projectsDb[pid].files[srcFile].imports = imports;
        storage.updateJsonDb();
    }

    //add import
    imports.forEach(function (item) {
        if (importsCollection[item]) {
            //has in importsCollection
            if (importsCollection[item].join().indexOf(srcFile) === -1) {
                importsCollection[item].push(srcFile);
            }
        } else {
            //add to importsCollection
            importsCollection[item] = [srcFile];
            watchImport(item);
        }
    });

    saveImportsCollection();
};

/**
 * remove imports
 * @param  {String} importedFile
 * @param  {Array}  invalidFile
 */
function removeImports(importedFile, invalidFile) {
    var fileslist = Array.isArray(invalidFile) ? invalidFile.join() : invalidFile;

    importsCollection[importedFile] = importsCollection[importedFile].filter(function (item) {
        return fileslist.indexOf(item) === -1;
    });

    saveImportsCollection();
}

/**
 * get watchedCollection
 * @return {Object}
 */
exports.getWatchedCollection = function () {
    return watchedCollection;
};

/**
 * get importCollection
 * @return {Object}
 */
exports.getImportsCollection = function () {
    return importsCollection;
};

/**
 * set importsCollection
 * @param {Obejct} importsDb importsCollection
 */
exports.setImportsCollection = function (importsDb) {
    importsCollection = importsDb;
};


/**
 * add watch listener
 * @param {String} src file src
 */
function addWatchListener(src) {
    if (importsCollection[src]) {
        return false;
    }

    if (watchedCollection[src]) {
        fs.unwatchFile(src);
    }

    fs.watchFile(src, {interval: 500}, function (curr) {
        if (curr.mode === 0) return false;

        //when file change,compile
        var file = watchedCollection[src];
        if (file.compile) compilersManager.compileFile(file);
    });
}

/**
 * remove watch listener
 * @param  {String} src file src
 */
function removeWatchListener(src) {
    if (!importsCollection[src] || importsCollection[src].length === 0) {
        fs.unwatchFile(src);
    }
}

/**
 * add watch listener to import file,when import file changed,compile self and src file
 * @param  {String} fileSrc import file src
 */
function watchImport(fileSrc) {
    if (Array.isArray(fileSrc)) {
        fileSrc.forEach(function (item) {
            watch(item);
        });
    } else {
        watch(fileSrc);
    }

    function watch(src) {
        //delete old listener
        if (watchedCollection[src]) {
            fs.unwatchFile(src);
        }

        fs.watchFile(src, {interval: 500}, function (curr) {
            if (curr.mode === 0) return false;

            compile(src);
        });
    }

    function compile (src, isImportProp) {
        if (!isImportProp) {
            //compile watched file
            var self = watchedCollection[src];
            if (self && self.compile) compilersManager.compileFile(self);
        }

        //compile imported parents
        var parents = importsCollection[src],
            invalidFile = [];

        if (!parents || parents.length === 0) return false;

        parents.forEach(function (item) {
            //If parent file is not exists, remove it.
            if (!fs.existsSync(item)) {
                invalidFile.push(item);
                return false;
            }

            //only compiling when the parent file had in watchedCollection
            var parent = watchedCollection[item];

            if (parent && parent.compile) {
                compilersManager.compileFile(parent);
            }

            compile(item, true);
        });

        //remove invalid file
        if (invalidFile.length > 0) {
            removeImports(src, invalidFile);
        }
    }
}
exports.watchImport = watchImport;

/**
 * save imports collection to imports.json
 */
function saveImportsCollection() {
    var imports = importsCollection;

    //Remove the empty value items
    for (var k in imports) {
        if (imports[k].length === 0) {
            delete imports[k];
        }
    }

    var jsonString = JSON.stringify(imports, null, '\t');

    storage.saveImportsDb(jsonString);
}

global.watchedCollection = watchedCollection;