
var fs    = require("fs");
var util  = require("util");
var iconv = require('iconv-lite');

var CRLF = "\r\n";

var logLevel = {
    'L_noLog'   : -1,
    'L_Low'     : 0,
    'L_Normal'  : 1,
    'L_High'    : 2,
    'L_Extended': 3,
    'L_Full'    : 4
};

var ___logLevel = {
    "0": 'L_Low',
    "1": 'L_Normal',
    "2": 'L_High',
    "3": 'L_Extended',
    "4": 'L_Full'
};

var CurrentLogLevel = logLevel.L_Normal;
var ConsoleFile = '';
var writeToFile = '';
var colors = {
    default: '0',
    cyan   : '36',
    lcyan  : '1;36',
    red    : '31',
    lred   : '1;31',
    yellow : '33',
    bWhite : '1'
};

Object.defineProperty(global, '__stack', {
    configurable: true,

    get: function () {
        var orig = Error.prepareStackTrace;

        Error.prepareStackTrace = function (_, stack) {
            return stack;
        };

        var err = new Error;

        if (Error.captureStackTrace) {
            Error.captureStackTrace(err, arguments.callee);
        }

        var stack = err.stack;

        Error.prepareStackTrace = orig;

        return stack;
    }
});

var traceStack = global.__stack;

function formatDate(formatDate, formatString) {
    var mcDate = Object.prototype.toString.call(formatDate) === '[object String]';

    if (mcDate) formatDate = formatDate.split('.');

    var yyyy = formatDate.getFullYear();
    var yy = yyyy.toString().substring(2);
    var m = formatDate.getMonth() + 1;
    var mm = m < 10 ? "0" + m : m;
    var d = formatDate.getDate();
    var dd = d < 10 ? "0" + d : d;

    var h = formatDate.getHours();
    var hh = h < 10 ? "0" + h : h;
    var n = formatDate.getMinutes();
    var nn = n < 10 ? "0" + n : n;
    var s = formatDate.getSeconds();
    var ss = s < 10 ? "0" + s : s;

    formatString = formatString.replace(/yyyy/i, yyyy);
    formatString = formatString.replace(/yy/i, yy);
    formatString = formatString.replace(/mm/i, mm);
    formatString = formatString.replace(/m/i, m);
    formatString = formatString.replace(/dd/i, dd);
    formatString = formatString.replace(/d/i, d);
    formatString = formatString.replace(/hh/i, hh);
    formatString = formatString.replace(/h/i, h);
    formatString = formatString.replace(/nn/i, nn);
    formatString = formatString.replace(/n/i, n);
    formatString = formatString.replace(/ss/i, ss);
    formatString = formatString.replace(/s/i, s);

    return formatString;
}

Date.prototype.myFormat = function (format) {
    return formatDate(this, format);
};

function PrintLine(source, title, _this, txt, enableLogWhere) {
    var trace  = getTrace(source[1]);
    var string = '[' + ((title && (typeof title === 'string') && (title.length > 0)) ? (title + ' ') : '');

    if (enableLogWhere == true) {
        var fullTrace = "";

        for (var tt = 1; tt < source.length; tt++) {
            var yy = getTrace(source[tt]);

            fullTrace += util.format("%s:%d\n", yy.file, yy.lineno);
        }

        string += util.format("%s]: %s\n", trace.timestamp, util.format.apply(_this, [txt])) + fullTrace;
    } else if ((enableLogWhere == false) || (enableLogWhere == undefined)) {
        string += util.format("%s]: %s", trace.timestamp, util.format.apply(_this, [txt]));
    }

    return string;
}

function getTrace(call) {
    var curDate = new Date();

    return {
        file: call.getFileName(),
        lineno: call.getLineNumber(),
        timestamp: curDate.myFormat('dd:mm:yyyy hh:nn:ss')
    };
}

function colourise(colourCode, string, background) {
    var backcolor = (!background) ? "\x1B[0m" : background;

    return "\x1B[" + colourCode + "m" + string + backcolor;
}

function ExtractPath(Path) {
    for (var i = Path.length - 1; i >= 0; i--) {
        if ((Path[i] === '\\') || (Path[i] === '/')) {
            return Path.slice(0, i);
        }
    }

    return Path;
}

function ExtractFileName(Path) {
    var count = 0;

    for (var i = Path.length - 1; i >= 0; i--) {
        count++;

        if ((Path[i] === '\\') || (Path[i] === '/')) {
            return Path.slice(i + 1, i + count);
        }
    }

    return Path;
}

function LogToFile(_text, file) {
    if (file || file !== '') {
        var _path = ExtractPath(file);
        var _file = ExtractFileName(file);

        writeToFile += _text;

        fs.stat(_path, function (err) {
            if (err) {
                process.stdout.write(colourise(colors.red, '  >> [err]: Path not found: ' + _path) + CRLF);
            }

            if (writeToFile != '') {
                fs.appendFile(err ? './' + _file : file, writeToFile + CRLF, function (err) { });

                writeToFile = '';
            }
        });
    }
}

function writeLog(stack, title, color, arg, immediately) {
    var _logLevel;// = CurrentLogLevel;
    var _txt      = arg[0];
    var _logWhere = false;
    var _logFile  = undefined;

    if (Object.prototype.toString.call(arg[1]) == '[object Object]') {
        if (arg[1].logLevel) _logLevel = arg[1].logLevel;
        if (arg[1].logWhere) _logWhere = arg[1].logWhere;
        if (arg[1].logFile)  _logFile  = arg[1].logFile;
    } else {
        if (arg.length > 1) {
            if (___logLevel[arg[arg.length - 1]]) {
                _logLevel = arg[arg.length - 1];
            }
        }

        if (arg[1] && (Object.prototype.toString.call(arg[1]) == '[object Boolean]')) {
            _logWhere = arg[1];
        }
    }

    if (immediately === true) {
        var text = PrintLine(stack, title, this, _txt, _logWhere);

        process.stdout.write(colourise(color, text) + CRLF);
        
        try {
            fs.appendFileSync(ConsoleFile || './CRASH.log', text + CRLF);
        } catch (e){}
    } else

    if (_logLevel != undefined ? parseInt(_logLevel) <= CurrentLogLevel : CurrentLogLevel >= 0) {
        var text = PrintLine(stack, title, this, _txt, _logWhere);

        process.stdout.write(colourise(color, text) + CRLF);

        // if (immediately) {
        //     try{
        //         fs.appendFileSync(ConsoleFile || './CRASH.log', text + CRLF);
        //     } catch (e){}
        // } else
        if (_logFile) {
            LogToFile(text, _logFile)
        } else {
            LogToFile(text, ConsoleFile);
        }
    }
}

function AddCustomLogFile(param) {} // todo: make this?

module.exports = {
    log : function () { writeLog.apply(this, [traceStack, 'log ', colors.default, arguments]) },
    info: function () { writeLog.apply(this, [traceStack, 'info', colors.lcyan, arguments]) },
    err : function () { writeLog.apply(this, [traceStack, 'err ', colors.lred, arguments]) },
    error : function () { writeLog.apply(this, [traceStack, 'err ', colors.lred, arguments]) },
    warn: function () { writeLog.apply(this, [traceStack, 'warn', colors.yellow, arguments]) },
    important: function () { writeLog.apply(this, [traceStack, '  ! ', colors.bWhite, arguments]) },
    wrt: {
        write: function () {
            writeLog.apply(this, [traceStack, 'wrt ', colors.bWhite, arguments]);
        }
    },

    immediately: function () { writeLog.apply(this, [traceStack, 'ERR ', colors.lred, arguments, true]) },

    DublicateToFile: function (fileName) { ConsoleFile = fileName; },

    AddCustomLofFile: AddCustomLogFile,

    logLevel: logLevel,
    SetLogLevel: function (ll) { CurrentLogLevel = ll; },
    GetLogLevel: function () { return CurrentLogLevel; }
};

// Colours
//
// \x1B[0m    все атрибуты по умолчанию
// \x1B[1m    жирный шрифт (интенсивный цвет)
// \x1B[2m    полу яркий цвет (тёмно-серый, независимо от цвета)
// \x1B[4m    подчеркивание
// \x1B[5m    мигающий
// \x1B[7m    реверсия (знаки приобретают цвет фона, а фон -- цвет знаков)
//
// \x1B[22m    установить нормальную интенсивность
// \x1B[24m    отменить подчеркивание
// \x1B[25m    отменить мигание
// \x1B[27m    отменить реверсию
//
// \x1B[30    чёрный цвет знаков
// \x1B[31    красный цвет знаков
// \x1B[32    зелёный цвет знаков
// \x1B[33    желтый цвет знаков
// \x1B[34    синий цвет знаков
// \x1B[35    фиолетовый цвет знаков
// \x1B[36    цвет морской волны знаков
// \x1B[37    серый цвет знаков
//
// \x1B[40    чёрный цвет фона
// \x1B[41    красный цвет фона
// \x1B[42    зелёный цвет фона
// \x1B[43    желтый цвет фона
// \x1B[44    синий цвет фона
// \x1B[45    фиолетовый цвет фона
// \x1B[46    цвет морской волны фона
// \x1B[47    серый цвет фона


