const isEqual = function(original, fn) {
  return function(val) {
    fn(val===original);
  }
};
const Store = function(cfg) {
  Observable.call(this);
  this._props = cfg || {};
};

Store.prototype = {
  experimental: false,
  _set: function(keys, val, pointer, path){
    let i, _i;
    let key = keys[path.length];

    if(pointer[key] !== val){
      // TODO make it work
      if(typeof val === 'object' && key in pointer){
        this._set(keys, val[key], pointer[key], path.concat(key));
      }

      pointer[ key ] = val;
      this.fire( 'change', key, val );
      this.fire( key, val );
    }
  },
  set: function(key, val) {
    if(!this.experimental)
      if(this._props[key] !== val){
        this._props[ key ] = val;
        this.fire( 'change', key, val );
        this.fire( key, val );
      }

    return this._set(
        typeof key === 'string' ? key.split('.') : key,
        val,
        this._props,
        []
    );
  },
  get: function(key, returnLastStore) {
    if(!this.experimental)
      return this._props[key];

    key = typeof key === 'string' ? key.split('.') : key;

    let ref = this,
        lastStore = ref, i, _i;

    for( i = 0, _i = key.length; i < _i; i++ ){
      if( ref instanceof Store ){
        ref = ref._props[key[i]];
        lastStore = ref;
      }else{
        ref = ref[key[i]];
      }

      if( !ref && i < key.length - 1 )
        return void 0;
    }

    if( returnLastStore )
      return { lastQObject: lastStore, result: ref };

    return ref;
  },

  sub: function(key, fn) {
    if(Array.isArray(key)){
      var wrap = () => fn.apply(this, key.map(key=>this.get(key)));

      for( var i = 0, _i = key.length; i < _i; i++ ){
        this.on(key[i], wrap)
      }
      wrap();
    }else{
      this.on( key, fn )
      fn( this.get( key ) );
    }
    return this;
  },
  equal: function(key, val, fn) {
    const wrap = isEqual(val, fn);
    this.on(key, wrap);
    wrap(this.get(key));
    return this;
  },
  bind: function (key) {
    return new StoreBinding(this, key);
  }
};
const StoreBinding = function(store, key){
  this.store = store;
  this.key = key;
};
StoreBinding.prototype = {
  sub: function (fn) {
    this.store.sub(this.key, fn);
  },
  set: function (val) {
    this.store.set(this.key, val);
  }
};
Store.prototype = Object.assign(new Observable, Store.prototype);

typeof module === 'object' && (module.exports = Store);