// var assert = require('assert');
var assert = require('chai').assert
const util = require('util');


var utils = require("../bbhverse-pb-wip/utils")
var js = utils.js;
const proxiedDescriptor = js.proxiedDescriptor;
const Ref = js.Ref;
const valueBackedAccessor = js.valueBackedAccessor;




describe('proxy', function() {    

  it('is not possible for', function() {
    console.log(js.nonproxyable)
    assert.ok(true)
  });

  it('is possible for', function() {
        
    console.log(js.proxyable)
    assert.ok(true)
    
  });
})


describe('mutableproxy', function() {    

  it('blank creation should be possible.', function() {
    var mutableProxy = js.mutableProxy.create();
    console.dir(mutableProxy)
    assert.ok(true)
  });

  it('target should be switchable', function() {
    
    var mutableProxy = js.mutableProxy.create({original : 'target at create time'});
    console.dir(mutableProxy)
    js.mutableProxy.setTarget({'switched' : 'switched'}, mutableProxy)
    assert.ok(true)
  });

  it('handler should be switchable', function() {
    
    var mutableProxy = js.mutableProxy.create({original : 'target at create time'});
    js.mutableProxy.setHandler({
      get : function(target, key){ 
          console.log('handler switch invoked : ')
          // console.dir(meta.target)
          // console.dir(arguments)
          var args = Array.prototype.slice.call(arguments, 0);
          args[0] = target;
          return target[key]
          return Reflect.apply(Reflect[key], target, args)
      
          // var args = Array.prototype.slice.call(arguments, 0);
          // args[0] = meta.target;
          // return Reflect[key](...args)
        }
    }, mutableProxy)
    console.dir(mutableProxy)
    assert.ok(true)
  });


  it('handler should be called', function() {
  
    var mutableProxy = js.mutableProxy.create();
    // console.dir(mutableProxy);
    // console.log(mutableProxy);
    // js.mutableProxy.setTarget({ switched : 'switched'}, mutableProxy)
    // console.dir(mutableProxy);
    // console.log(mutableProxy);
    var x = {}

    js.mutableProxy.setHandler({
      get : function(target, key){ 
          console.log('handler switch invoked : ')
          // console.dir(meta.target)
          // console.dir(arguments)
          var args = Array.prototype.slice.call(arguments, 0);
          args[0] = target;
          x = 'handler was called'
          
          return target[key]
          return Reflect.apply(Reflect[key], target, args)
      
          // var args = Array.prototype.slice.call(arguments, 0);
          // args[0] = meta.target;
          // return Reflect[key](...args)
        }
    }, mutableProxy)
    
    console.dir(mutableProxy);
    console.log(mutableProxy);
    js.mutableProxy.setTarget({ switched : 'handlerswitched'}, mutableProxy)
    console.dir(mutableProxy);
    console.log(mutableProxy);

    assert.strictEqual('handler was called', x), 'but is not';
  });
})

describe('proxiedDescriptor', function() {  
  var o = {}
  Object.defineProperty(o, 'vV', proxiedDescriptor.create()); 
  
  it('on create should be undefined', function() {
    // console.log(o.vV)
    // console.dir(o.vV)  
    assert.isUndefined(o.vV);
  });
  
  it('after revision current should be {a : 4}', function() {

    var x = { a: 5 }
    o.vV = x;

    console.dir(o.vV)
    // console.log("" + (o.vV.current=== x))
    assert.deepEqual(o.vV, x);
    // assert.strictEqual(o.vV.current, x), 'but is not';
    x = { a: 4 }
    o.vV.revision = x
    console.dir(o.vV)
    assert.deepEqual(o.vV, x);
    // assert.strictEqual(o.vV.previous, x), 'but is not';
  });

  it('valueBackedAccessor is unrevisioned unless revision is explicitly called.', function() {

    var vValue = js.vValue;
    
    var o1 = {}
    Object.defineProperty(o1, 'vV', valueBackedAccessor.create()); 
    
    // initial uninitialized state...
    console.log('console.log(cloned.vV)')
    console.log(o1.vV)
    console.log(Object.prototype.toString.call(o1.vV))
    console.log('---------------------------------------------')
    console.log('console.dir(o1.vV)')
    console.dir(o1.vV)
    console.log(Object.prototype.toString.call(o1.vV))
    // throw 'done' 
    console.log('---------------------------------------------')
    console.log('util.inspect(o1.vV)')
    console.log(util.inspect(o1.vV))
    console.log(Object.prototype.toString.call(o1.vV))
    console.log('---------------------------------------------')
    
    
    o1.vV = { d : 7 } 
    o1.vV = { e : 8 }
    o1.vV = { f : "9 Nine" }
    var ten = { g : "10" }
    o1.vV = ten
    
    // console.log(o1.vV === ten)
    assert.deepEqual(o1.vV, ten);
    assert.strictEqual(o1.vV.current, ten);
    assert.strictEqual(o1.vV, ten), 'but is not vV[Proxy] cannot be equal to original object. Use vv[proxy].current === original value for comparison. ';
  });


  it('proxiedDescriptor switch to composite revision', function() {
      
    var ten = { g : "10" }
    // proxiedDescriptor.ten = ten;
    var o1 = js.Versioned.create();
    // Object.defineProperty(o1, 'vV', proxiedDescriptor.create(proxiedDescriptor.current)); 
    // o1.vV = null;

    // initial uninitialized state...
    console.log('console.log(cloned.vV)')
    console.log(o1.vV)
    console.log(Object.prototype.toString.call(o1.vV))
    console.log('---------------------------------------------')
    console.log('console.dir(o1.vV)')
    console.dir(o1.vV)
    console.log(Object.prototype.toString.call(o1.vV))
    // throw 'done' 
    console.log('---------------------------------------------')
    console.log('util.inspect(o1.vV)')
    console.log(util.inspect(o1.vV))
    console.log(Object.prototype.toString.call(o1.vV))
    console.log('---------------------------------------------')

    o1.vV = { d : 7 } 

    console.dir(o1.vV)
    o1.vV = { e : 8 }
    console.dir(o1.vV)
    o1.vV = { f : "9 Nine" }
    console.dir(o1.vV)
    debugger
    o1.vV = ten
    var a = o1.vV;
    o1.vV = ten

    // console.log(util.inspect(o1.vV))
    // console.log((o1.vV === ten))
    console.dir(o1.vV)
    console.dir(o1.vV.g)
    assert.deepEqual(o1.vV, ten);
    assert.strictEqual(o1.vV, a); // Self identity via proxy === proxy. Instead of target === target which is effectively the same..

    console.dir((ten == (+o1.vV)))  // false Doesn't work. Throws exception when valueOf and toString return non primitives.
    console.dir((ten === o1.vV))  // false Doesn't work. coz getter always returns a proxy and not the target. The target indeed is === ten.
    // The options therefore as alternative or === which fails for proxies are as below. 
    // Explicitly call valueOf or use a helper which gets the real target of the proxy 
    console.dir((o1.vV.valueOf() === ten))
    assert.strictEqual(o1.vV.valueOf(), ten);
    assert.strictEqual(js.Proxied.getTarget(o1.vV), ten);
    // assert.strictEqual(o1.vV, ten);

    var d = Object.getOwnPropertyDescriptor(o1, "vV");
    proxiedDescriptor.switchTo(d, proxiedDescriptor['composite revision'])
    o1.vV = { eleven : '11'}
    console.dir(o1.vV)
    assert.deepEqual(o1.vV, { g : "10", eleven : '11' } );
  });

  it('series of changes should match', function() {

    o.vV = { d : 7 } 
    o.vV.revision = { b: 6 }
    o.vV.firstrevision = { c: 4 }

    // console.log("" + (o.vV.current=== x))
    // console.dir(o.vV)
    assert.deepEqual(o.vV.current, {b : 6} );
    o.vV.revision = { a : '4'}
    assert.deepEqual(o.vV.previous, {b : 6});
  });
  
});

describe('Ref', function() {  
  var o = { a : "aaaaa", b : "bbbbb" }
  
  var ref = js.Ref.create(o,'a')

  // console.log('---------------------s assignment---')
  var aaa = ref;
  // console.log('---------------------e assignment---')
  // console.dir(aaa)

  it('ref should return transparent proxy', function() {
    assert.strictEqual(ref, aaa, 'but does not');
  });

  it('ref() should return referenced value', function() {
    assert.deepEqual(ref(), 'aaaaa', 'but does not');
  });

}); 

describe('exercise some calls', function() {  

  it('series of calls should not throw exceptions.', function() {
          
    var cobj = { d : 'd', e : 5};
    var original = { a : 'a', b : 'b', cobj : cobj
      , arrayContainingObjects : [ { o: 'o', arr : [1,2]}, cobj ]
      , objectContainingArrays : { arr0 : ['o', 1, 'asd'], arr1 : [1,2]}
    };
    // PB : TODO -- Test accessor descriptor and valueBackedAccessorDescriptor cases.
    original.fobj = original.cobj;
    original.orig = original;
    
    Object.defineProperty(original, 'vba', js.valueBackedAccessor.create());
    original.vba = 'value set using setter'
    console.log('original.vba : ')
    console.dir(original.vba)
    console.dir(original)
    var d = Object.getOwnPropertyDescriptor(original, 'vba')
    // console.log('valueBackedAccessor.isValueBackedAccessor(d)' + js.valueBackedAccessor.isValueBackedAccessor(d));
    // console.log('valueBackedAccessor.isValueBackedAccessor(d)' + js.valueBackedAccessor.isValueBackedAccessor({}));
    
    var cloned = js.clone(false, original);
    console.log('cloned.vba : ')
    console.dir(cloned.vba)
    console.dir(cloned)
    console.log("isClone = " + (original !== cloned))
    console.log("isClone = " + (original.cobj !== cloned.cobj))
    console.log("isClone = " + (original.fobj !== cloned.fobj))
    console.log("isClone = " + (original.cobj === original.fobj)) 
    console.log("isClone = " + (cloned.cobj === cloned.fobj)) // Ref cloning.
    
    console.log("descriptorRetrivalYieldsSameDescriptor = " + (Object.getOwnPropertyDescriptor(original, 'a') === Object.getOwnPropertyDescriptor(original, "a")))
    
    var linkedUsingAssign = {}
    js.assign(linkedUsingAssign, cloned, cloned)
    console.log('linkedUsingAssign.vba : ')
    
    console.dir(linkedUsingAssign.vba)
    console.dir(linkedUsingAssign)
    
    // var a = {
    //   valueOf : function(){ return 'I am a'}
    //   , toString : function(){ return 'I am a'}
    // }
    // console.dir('' + a)
    
    // vValueHelper Tests
    // var proxiedDescriptor = js.proxiedDescriptor;
    var vValue = js.vValue;
    
    Object.defineProperty(cloned, 'vV', vValue.create()); 
    console.log('vValueHelper')
    
    // initial uninitialized state...
    console.log('console.log(cloned.vV)')
    console.log(cloned.vV)
    console.log(Object.prototype.toString.call(cloned.vV))
    console.log('---------------------------------------------')
    console.log('console.dir(cloned.vV)')
    console.dir(cloned.vV)
    console.log(Object.prototype.toString.call(cloned.vV))
    // throw 'done' 
    console.log('---------------------------------------------')
    console.log('util.inspect(cloned.vV)')
    console.log(util.inspect(cloned.vV))
    console.log(Object.prototype.toString.call(cloned.vV))
    console.log('---------------------------------------------')
    
    cloned.vV = { d : 7 } 
    // cloned.vV = { e : 8 }
    // cloned.vV = { f : "9 Nine" }
    // cloned.vV = { g : "10" }
    
    cloned.vV.revision = { a: 5 }
    cloned.vV.revision = { a: 6 }
    cloned.vV.firstrevision = { c: 4 }
    cloned.vV.previous = { c0: 'c0' }
    cloned.vV.previous = { a: '4' }
    
    console.log('console.log(cloned.vV)')
    console.log(cloned.vV)
    console.log('---------------------------------------------')
    
    console.dir('console.dir(cloned.vV)')
    console.dir(cloned.vV)
    console.log('---------------------------------------------')
    
    console.log('util.inspect(cloned.vV)')
    console.log(util.inspect(cloned.vV))
    console.log('---------------------------------------------')
    
    console.log('console.dir(cloned.vV.composite)')
    console.dir(cloned.vV.composite)
    console.dir(cloned.vV.previous)
    console.dir(cloned.vV.current)
  });

});