import * as _ from 'util';
import * as caller from 'caller';
import * as moment from 'moment';
import {EventEmitter} from 'events';
import {dirname} from 'path';
import {existsSync, writeFileSync, appendFileSync, writeFile} from 'fs';
import {Map} from './types/lang';

export const E_NOTICE: number = 1;
export const E_DEBUG: number = 2;
export const E_WARN: number = 4;
export const E_FATAL: number = 8;
export const E_ALL: number = 15;

export class Logger extends EventEmitter{
    logFile: string;
    level: number;
    buffer: Array<string> = [];
    useBuffer: boolean = false;

    constructor(options: Map<any>) {
        super();

        if (!options['logFile']) {
            throw new Error('must given `logFile`.');
        }

        this.logFile = options['logFile'];
        this.level = options['level'] || E_ALL;
        
        var __this = this;

        ['notice', 'debug', 'warn', 'fatal'].forEach(function (fn) {
            __this.on(fn, __this.to(fn)); 
        });
    }

    public to(fn) {
        var __this = this;
        return function (...args) {
            __this[fn](...args);
        };
    }

    private writeSync(path:string, data:string) {
        if (!existsSync(path)) {
            writeFileSync(path, data);
        } else {
            appendFileSync(path, data);
        }
    }

    private write(path:string, data:string, callback) {
        writeFile(path, data, callback);
    }

    protected logging(tag:string, level:number, format:string, ...args) {
        if (!(this.level & level)) {
            return;
        }

        format = `[${tag}] ${moment().format('YYYY-MM-DD HH:mm:ss')} ${format}\n`;

        if (this.useBuffer) {
            this.buffer.push(_.format(format, ...args));
        } else {
            let logFile = (level & (E_FATAL | E_WARN)) ? this.logFile + '.wf' : this.logFile;
            this.writeSync(logFile, _.format(format, ...args));
        }
    }

    public notice(format:string, ...args) {
        return this.logging('NOTICE', E_NOTICE, format, ...args);
    }

    public debug(format:string, ...args) {
        return this.logging('DEBUG', E_DEBUG, format, ...args);
    }

    public warn(format:string, ...args) {
        return this.logging('WARN', E_WARN, format, ...args);
    }

    public fatal(format:string, ...args) {
        return this.logging('FATAL', E_FATAL, format, ...args);
    }
}

function logFileByCaller():string {
    return dirname(caller(2)) + '/kite.log'
}

export function logger() {
    return new Logger({
        logFile: logFileByCaller(),
        level: E_ALL
    });
};