/**
 * log.js
 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * DongHee Yang <donghee.yang@samsung.com>
 * Sungmin Kim <sm.art.kim@samsung.com>
 * Jiil Hyoun <jiil.hyoun@samsung.com>
 * Jonghwan Park <iwin100.park@samsung.com>
 * Kitae Kim <kt920.kim@samsung.com>
 *
 * 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.
 *
 * Contributors:
 * - S-Core Co., Ltd
**/
var async = require('async');
var util = require('util');
var winston = require('winston');
var mongojs = require('mongojs');
var dibs = require('./dibs.js');
var DError = require('./exception');
var DHandler = require('./error-handler.js');
require('winston-mongodb').Mongo;
require('date-format-lite');


module.exports = new LogSystem();

function LogSystem() {
    this.open = function (logId, options) {
        if (!options) {
            options = {};
        }

        var isDebug = options.debug;

        return new Logger(logId, isDebug, options);
    };

    this.info = function (message) {
        dibs.thisServer.log.info(message);
    };

    this.warn = function (message) {
        dibs.thisServer.log.warn(message);
    };

    this.error = function (message) {
        dibs.thisServer.log.error(message);
    };

    this.openMonitor = function (options) {
        if (!options) {
            options = {};
        }

        var isDebug = options.debug;

        return new Log(isDebug, options);
    };
}

function Log(debugFlag, options) {
    var db = null;
    var cursor = null;

    try {
        db = connectMongoDB(options);
    } catch (e) {
        throw new DError('LOG001', e);
    }

    this.monitor = function (collectionName, condition, stream, callback) {
        if (!db) {
            return callback(new DError('LOG001'));
        }

        var collection = db.collection(collectionName);
        var capped;

        async.series([
            function (scb) {
                collection.isCapped(function (err, res) {
                    capped = res;
                    scb(err);
                });
            },
            function (scb) {
                if (!capped) {
                    db.runCommand({
                        convertToCapped: collectionName,
                        size: 1000000
                    }, function (err) {
                        return scb(err);
                    });
                } else {
                    return scb(null);
                }
            },
            function (scb) {
                condition = condition || {};
                cursor = collection.find(condition, {}, {
                    tailable: true,
                    timeout: false
                });

                if (!cursor) {
                    return scb(new DError('LOG002'));
                }

                cursor.on('data', function (data) {
                    stream(data);
                });

                cursor.on('error', function (err) {
                    callback(err);
                });

                cursor.on('end', function () {
                    if (cursor) {
                        cursor.destroy();
                    }
                    callback(null);
                });
                scb(null);
            }
        ], function (err) {
            if (err) {
                console.error(err);
                return callback(err);
            }
        });
    };

    function connectMongoDB(options) {
        if (options.remoteLogDbHost && options.remoteLogDbName) {
            var dbName = options.remoteLogDbName;
            var dbHost = options.remoteLogDbHost;
            var dbPort = options.remoteLogDbPort || 27017;

            return mongojs(dbHost + ':' + dbPort + '/' + dbName);
        } else {
            return null;
        }
    }

    this.getLastNLine = function (collectionName, condition, lineNumber, cb) {
        if (!db) {
            var error = new DError('LOG001');
            cb(error);
            return;
        }

        var collection = db.collection(collectionName);

        condition = condition || {};
        collection.find(condition).sort({
            $natural: -1
        }).limit(lineNumber, function (err, docs) {
            if (err) {
                return cb(err, null);
            } else {
                return cb(err, docs);
            }
        });
    };

    this.close = close;

    function close() {
        if (cursor) {
            cursor.destroy();
            cursor = null;
        }

        if (db) {
            db.close();
            db = null;
        }
    }
}

function Logger(logId, debugFlag, options) {
    // generate winston option
    var winstonOptions = getDefaultConfiguration(logId, options);

    var wLogger = null;

    var wLoggerClose = null;

    try {
        wLogger = new (winston.Logger)(winstonOptions);
    } catch (e) {
        throw new DError('LOG001', e);
    }
    wLogger.on('error', function (err) {
        console.error(err);
    });

    this.info = function (message) {
        write('INFO', message);
    };


    this.warn = function (message) {
        write('WARN', message);
    };


    this.error = function (message) {
        write('ERROR', message);
    };

    this.getLogFilePath = function () {
        if (options && options.filename) {
            return options.filename;
        } else {
            return null;
        }
    };


    function close() {
        var handler = null;
        if (wLogger && !wLoggerClose) {
            wLoggerClose = true;
            if (wLogger.transports && wLogger.transports.mongodb && wLogger.transports.mongodb.logDb) {

                handler = {
                    try: function () {
                        wLogger.transports.mongodb.logDb.close();
                    },
                    catch: function (e) {
                        console.log(e);
                    }
                };
                DHandler.run(handler);
            } else {
                handler = {
                    try: function () {
                        wLogger.close();
                    },
                    catch: function (e) {
                        console.log(e);
                    }
                };
                DHandler.run(handler);
            } }
        wLogger = null;
    }

    this.close = function () {
        close();
    };

    function write(type, message) {
        var msg = util.inspect(message);
        if (msg instanceof Buffer) {
            msg = msg.toString();
        }

        if (wLogger && !wLoggerClose) {
            try {
                if (type === 'WARN') {
                    wLogger.warn(logId + ': ' + msg);
                } else if (type === 'ERROR') {
                    wLogger.error(logId + ': ' + msg);
                    if (message.stack) {
                        wLogger.error(logId + ': ' + message.stack);
                    }
                } else {
                    wLogger.info(logId + ': ' + msg);
                }
            } catch (e) {
                console.error('[LOGGER ERROR]', e);
            }
        } else {
            if (type === 'ERROR') {
                console.error(message);
            } else {
                console.log(message);
            }
        }
    }


    function getDefaultConfiguration(logId, options) {
        var transports = [];

        if (options.filename) {
            var filename = options.filename;
            var json = options.json || false;
            transports.push(new (winston.transports.File)({
                filename: filename,
                maxsize: 100000000,
                maxFiles: 10,
                timestamp: function () {
                    var now = new Date();
                    return now.format('YYYY-MM-DD hh:mm:ss.SS');
                },
                handleExceptions: true,
                json: json
            })
            );
        }

        if (options.remoteLogDbHost && options.remoteLogDbName) {
            transports.push(new (winston.transports.MongoDB)({
                //host: options.remoteLogDbHost,
                db: 'mongodb://' + options.remoteLogDbHost + '/' + options.remoteLogDbName,
                collection: logId,
                capped: true
            })
            );
        }

        if (!options.filename && (!options.remoteLogDbHost || !options.remoteLogDbName)) {
            transports.push(new (winston.transports.Console)({
                handleExceptions: true,
                colorize: false
            }));
        }

        return {
            transports: transports,
            exceptionHandlers: [new winston.transports.File({
                filename: 'logException.log'
            })],
            exitOnError: false
        };
    }
}
