(function() {
    var qyutil = {};
    var fs = require("fs"),
        path = require("path");

    function ensureDir(dir) {
        if (fs.existsSync(dir)) {
            return;
        }
        var parent = path.dirname(dir);
        ensureDir(parent);
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir);
        }
    };
    qyutil.ensureDir = ensureDir;

    function isdir(p) {
        var stat = fs.statSync(p);
        return stat && stat.isDirectory();
    };
    qyutil.isdir = isdir;
    qyutil.getAllFiles = function(root, dir) {
        fullPath = path.join(root, dir);
        if (!isdir(fullPath)) {
            return [dir];
        }
        var items = fs.readdirSync(fullPath),
            ret = [];
        for (var i = 0; i < items.length; i++) {
            var one = items[i],
                p = path.join(dir, one);
            ret = ret.concat(qyutil.getAllFiles(root, p));
        };
        return ret;
    };
    qyutil.expandFolder = function(folder) {
        var ret = {
            files: [],
            folders: []
        };
        if (fs.existsSync(folder) && isdir(folder)) {
            var all = fs.readdirSync(folder);
            for (var i = 0; i < all.length; i++) {
                var one = all[i];
                if (isdir(path.join(folder, one))) {
                    ret.folders.push(one);
                } else {
                    ret.files.push(one);
                }
            }
        }
        return ret;
    };
    qyutil.num2char = function(num) {
        var ret;
        if (num >= 0 && num < 10) {
            ret = num + '0'.charCodeAt(0);
        } else if (num >= 10 && num < 36) {
            ret = num - 10 + 'A'.charCodeAt(0);
        } else {
            ret = num - 36 + 'a'.charCodeAt(0);
        }
        return String.fromCharCode(ret);
    };
    qyutil.char2num = function(char) {
        if (char >= '0' && char <= '9') {
            return char.charCodeAt(0) - '0'.charCodeAt(0);
        } else if (char >= 'A' && char <= 'Z') {
            return char.charCodeAt(0) - 'A'.charCodeAt(0) + 10;
        } else {
            return char.charCodeAt(0) - 'a'.charCodeAt(0) + 36;
        }
    };

    qyutil.mt2Date = function(mt) {
        var date = new Date();
        date.setFullYear(qyutil.char2num(mt[0]) + 2000);
        date.setMonth(qyutil.char2num(mt[1]) - 1);
        date.setDate(qyutil.char2num(mt[2]));
        date.setHours(qyutil.char2num(mt[3]));
        date.setMinutes(qyutil.char2num(mt[4]));
        date.setSeconds(qyutil.char2num(mt[5]));
        return date;
    };
    qyutil.date2MT = function(date) {
        return qyutil.num2char(date.getFullYear() - 2000) +
            qyutil.num2char(date.getMonth() + 1) +
            qyutil.num2char(date.getDate()) +
            qyutil.num2char(date.getHours()) +
            qyutil.num2char(date.getMinutes()) +
            qyutil.num2char(date.getSeconds());
    };
    qyutil.getMT = function(file) {
        if (!fs.existsSync(file)) {
            return '0'
        }
        var date = fs.statSync(file).mtime;
        return qyutil.date2MT(date);
    };
    qyutil.setMT = function(file, mt) {
        var date = qyutil.mt2Date(mt);
        fs.utimesSync(file, date, date);
    };

    qyutil.getLogLine = function(argList) {
        var date = new Date();
        var args = [date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate() + "," +
            date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds()
        ];
        for (var i = 0; i < argList.length; i++) {
            var a = argList[i];
            if (typeof a == "object") {
                a = JSON.stringify(a);
            }
            args.push(a);
        }
        return args.join(" ");
    };

    function getLine() {
        var e = new Error();
        // now magic will happen: get line number from callstack
        var line = e.stack.split('\n')[3].split(':')[1];
        return line;
    }

    qyutil.logger = function(module) {
        var fn = path.basename(module.filename);
        var ret = {
            info: function() {
                console.log.apply(console, arguments);
            },
            debug: function() {
                var args = [fn + ":" + getLine() + ":"];
                for (var i = 0; i < arguments.length; i++) {
                    args.push(arguments[i]);
                }
                console.log.apply(console, args);
            },
            dir: function() {
                var args = [fn + ":" + getLine() + ":"];
                for (var i = 0; i < arguments.length; i++) {
                    args.push(arguments[i]);
                }
                console.dir.apply(console, args);
            }
        };
        ret.log = ret.debug;
        return ret;
    };
    var logger = qyutil.logger(module);
    var deleteFolderRecursive = function(folder) {
        var files;
        if (fs.existsSync(folder)) {
            files = fs.readdirSync(folder);
            files.forEach(function(file) {
                var curPath = path.join(folder, file);
                if (isdir(curPath)) { // recurse
                    deleteFolderRecursive(curPath);
                } else { // delete file
                    fs.unlinkSync(curPath);
                }
            });
            fs.rmdirSync(folder);
        }
    };
    qyutil.deleteFolderRecursive = deleteFolderRecursive;

    function checkFolderNewerThan(folder, mt) {
        if (!fs.existsSync(folder)) {
            return false;
        }
        var files = fs.readdirSync(folder),
            folders = [];
        for (var i = 0; i < files.length; i++) {
            var one = path.join(folder, files[i]);
            if (isdir(one)) {
                folders.push(one);
            } else {
                if (fs.statSync(one).mtime > mt) {
                    return true;
                }
            }
        }
        for (i = 0; i < folders.length; i++) {
            one = folders[i];
            if (checkFolderNewerThan(one)) {
                return true;
            }
        }
        return false;
    }
    qyutil.checkFolderNewerThan = checkFolderNewerThan;

    function readSrcLength(src){
        var length = 0;
        srcLength(src);
        function srcLength(src){
            if(isdir(src)){
                var files = fs.readdirSync(src);
                for (var i = 0; i < files.length; i++) {
                    var one = files[i];
                    srcLength(path.join(src, one));
                }
            }else{
                length++;
            }
        }
        
        return length;
    }
    qyutil.readSrcLength = readSrcLength;

    function copy(src, dest, force,callback) {
        if (!fs.existsSync(src)) {
            return;
        }
        if (isdir(src)) {
            ensureDir(dest);
            var files = fs.readdirSync(src);
            for (var i = 0; i < files.length; i++) {
                var one = files[i];
                copy(path.join(src, one), path.join(dest, one), force,callback);
            }
        } else {
            var stat1 = fs.statSync(src);
            if (!force && fs.existsSync(dest)) {
                var stat2 = fs.statSync(dest);
                if (stat1.mtime <= stat2.mtime) {
                    return;
                }
            }
            ensureDir(path.dirname(dest));
            if(callback){
                fs.writeFile(dest, fs.readFileSync(src),function(err){
                    if(err) throw err;
                    callback();
                });
            }else{
                fs.writeFileSync(dest, fs.readFileSync(src));
            }
            if (!force) {
                fs.utimesSync(dest, stat1.atime, stat1.mtime);
            }
        }
    }
    qyutil.copy = copy;

    function checkPathSafety(root, folderPath) {
        var ret = path.join(root, folderPath);
        if (ret == path.resolve(ret)) {
            return ret;
        } else {
            return null;
        }
    }
    qyutil.checkPathSafety = checkPathSafety;
    module.exports = qyutil;
})();