"use strict";
var env = require('./env');
var Spec = require('./Spec');
var Storage = require('./Storage');

/**
 * Adaptor for Redis
 * @param {{redis:object, redisConnectParams:{unixSocket:string?, port:number?, host:string?, options:object}}} options
 *
 * <code>
 *      var storage = new Swarm.RedisStorage('dummy', {
 *          redis: require('redis'),
 *          redisConnectParams: {
 *              port: 6379,
 *              host: '127.0.0.1',
 *              options: {}
 *          }
 *      });
 *      storage.open(callback);
 * </code>
 *
 * @TODO storage opening by host
 */
function RedisStorage (id, options) {
    Storage.call(this);
    this.options = options;
    this._host = null; // will be set by the Host
    this.redis = options.redis;
    this.redisConnectParams = options.redisConnectParams || {
        unixSocket: undefined,
        port: 6379,
        host: '127.0.0.1',
        options: {}
    };
    this.db = null;
    this._id = id;
    this.logtails = {};
}
RedisStorage.prototype = new Storage();
module.exports = RedisStorage;
RedisStorage.prototype.isRoot = env.isServer;

var TAIL_FIELD_SUFFIX = ":log";

RedisStorage.prototype.open = function (callback) {
    var params = this.options.redisConnectParams;
    if (params.unixSocket) {
        this.db = this.redis.createClient(params.unixSocket, params.options || {});
    } else {
        this.db = this.redis.createClient(params.port || 6379, params.host || '127.0.0.1', params.options || {});
    }
    this.db.once('ready', callback);
};

RedisStorage.prototype.writeState = function (spec, state, cb) {
    console.log('>STATE',state);
    var self = this;
    var ti = spec.filter('/#');
    //var save = JSON.stringify(state, undefined, 2);
    if (!self.db) {
        console.warn('the storage is not open', this._host && this._host._id);
        return;
    }

    var json = JSON.stringify(state);
    var cleanup = this.logtails[ti] || [];
    delete this.logtails[ti];

    console.log('>FLUSH',json,cleanup.length);
    self.db.set(ti, json, function onSave(err) {
        if (!err && cleanup.length && self.db) {
            console.log('>CLEAN',cleanup);
            cleanup.unshift(ti + TAIL_FIELD_SUFFIX);
            self.db.hdel(cleanup, function (err, entriesRemoved) {
                err && console.error('log trimming failed',err);
            });
        }
        err && console.error("state write error", err);
        cb(err);
    });

};

RedisStorage.prototype.writeOp = function (spec, value, cb) {
    var ti = spec.filter('/#');
    spec = spec.toString();
    var json = JSON.stringify(value);
    console.log('>OP', spec, json);

    // store spec in logtail
    var log = this.logtails[ti] || (this.logtails[ti] = []);
    log.push(spec);
    // save op in redis
    var logFieldName = ti + TAIL_FIELD_SUFFIX;
    this.db.hset(logFieldName, spec, json, function (err) {
        err && console.error('op write error',err);
        cb(err);
    });
};

RedisStorage.prototype.readState = function (ti, callback) {
    var self = this;
    this.db.get(ti.toString(), function (err, value){
        if (err) {
            return callback(err);
        }

        if (!value) {
            value = {_version: '!0'};
        } else {
            value = JSON.parse(value);
        }

        console.log('<STATE', self._host && self._host._id, value);
        callback(null, value);
    });
};

RedisStorage.prototype.readOps = function (ti, callback) {
    var self = this;
    var fieldName = ti + TAIL_FIELD_SUFFIX;
    this.db.hgetall(fieldName, function (err, res) {
        if (err) {
            return callback(err);
        }
        console.log('<TAIL', self._host && self._host._id, tail);
        var tail = {};
        var log =  self.logtails[ti] || (self.logtails[ti] = []);
        if (res) {
            for (var spec in res) {
                var vo = Spec.as(spec).filter('!.');
                tail[vo] = JSON.parse(res[spec]);
                log.push(spec);
            }
        }
        callback(null, tail);
    });
};

RedisStorage.prototype.off = function (spec,val,src) {
    var ti = spec.filter('/#');
    delete this.logtails[ti];
    Storage.prototype.off.call(this, spec, val, src);
};

RedisStorage.prototype.close = function (callback,error) { // FIXME
    if (error) {
        console.log("fatal IO error", error);
    }
    if (this.db) {
        this.db.unref();
        this.db.get('fakeField', function (err, val) {
            callback(err);
        });
        this.db = null;
    } else {
        callback(); // closed already
    }
};

@@ -2545,24 +2545,56 @@
 lter('/#');%0A
+    var vo = spec.filter('!.');%0A
     spec = s
@@ -2783,28 +2783,26 @@
    log.push(
-spec
+vo
 );%0A    // sa
@@ -2894,20 +2894,18 @@
 ldName, 
-spec
+vo
 , json, 
@@ -3875,20 +3875,18 @@
 or (var 
-spec
+vo
  in res)
@@ -3892,61 +3892,8 @@
 ) %7B%0A
-                var vo = Spec.as(spec).filter('!.');%0A
     
@@ -3930,20 +3930,18 @@
 rse(res%5B
-spec
+vo
 %5D);%0A    
@@ -3961,20 +3961,18 @@
 og.push(
-spec
+vo
 );%0A     

use "!id.op" (not full-spec) as a key in log-hash