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;


var vValue = js.vValue;
    

// var o = {}
// Object.defineProperty(o, 'vV', proxiedDescriptor.create()); 
// var x = { a: 5 }
//     o.vV = x;

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





console.dir(Reflect.ownKeys(Reflect));


// Find proxyable types
(()=>{
  utils.js.typeInstances.forEach((instance)=>{
    try {
      new Proxy(instance,{})
      js.proxyable.push(js.nativeType(instance))
    }
    catch(e){
      js.nonproxyable.push(js.nativeType(instance))
      // console.log('Proxy not possible for : ' + js.nativeType(instance))
      // console.log(e);
    }  
  })
  return;
})()
console.dir('console.dir(js.proxyable)');
console.dir(js.proxyable)
console.dir('console.dir(js.nonproxyable)');
console.dir(js.nonproxyable);


// Getter Setter test.
var vstore = ''; 
var r = {}
Object.defineProperty(r, 'p',{ get : function(){ return 'u got me : ' + vstore }, set : function(v){  vstore = v}, enumerable:true })
console.dir(r)
console.dir('***************-------- ')
r.p = 'Pvalue'
var ref = js.Ref.create(r, 'p')
console.dir(ref)
ref.y ='sadasdf'
console.dir(ref)
ref.assign('u set me')
ref.x = 'asd'
console.dir(ref)
console.dir(r)


r = {}
ref = js.Ref.create(r, 'p')
console.dir('***************-------- ')
console.dir(ref)
console.dir(typeof(ref))
r.p = 'initialP'
console.dir(ref)
console.dir(typeof(ref))

// PB : TODO -- Test
// r.p preexisting value or with data descriptor
// r.p with accessor that has side effects.
// r.p is an object.



r = { p : 'p'}
ref = js.Ref.create(r, 'p')
console.dir('***************-------- ')
console.dir(ref)
console.dir(typeof(ref))
console.dir(typeof(new String('abc')))
// console.dir('ref()  -------- ')
// console.dir(ref())
// console.dir('ref()  -------- ')
ref.assign("P")
console.dir(ref)
// console.dir(Object.prototype.toString.call('astring'))
console.dir(r)  
ref.assign("PP")

console.dir(ref)
console.dir(Object.prototype.toString.call(ref))
console.dir(new String('asdfasdf'))
console.dir(Object.prototype.toString.call(new String('asdfasdf')))
// console.dir(Object.prototype.toString.call(new Proxy(new String('rawproxy'), {})))
console.dir(r)

r.p = "original replaced"
console.dir(ref)
console.dir(r)


// PB : TODO -- enable when ref extensions are implemented.
console.dir('ref.x')
console.dir(ref.x)  
ref.x = 'X';
console.dir(ref.x)  
console.dir(ref)
console.dir(r)
console.dir(ref.substring(2,5))

r.p = 'x after pchanged'
// ref.assign(Object.assign(new String('sdsd'), { p : r.p}) )
console.dir(ref.x)  
console.dir(ref) 
console.dir(r)

ref.assign(new String('ref.assign'));
console.dir(ref.x)  
console.dir(ref)
console.dir(r)

var ns = new String('sdsd')
console.dir(ns)
ns.x = 'X'
console.dir(ns)


// ref.p = "PLOP"
// console.dir(r)
// console.dir(ref.p)




// var o = js.Versioned.create(proxiedDescriptor['versioned current'])
    var o = js.Versioned.create()

    // o = 5; // This will destroy the reference to the prooxy received from Versioned.create.
    // we cannot override assignment operator... 
    // therefore provision made for assign etc. methods for these types at the object level.
    // to achieve a switch to the underlying content for the reference.
    // property level assignment is possible through accessors or proxied descriptors.
    o.assign(new String('ddd'));
    console.dir('console.dir(o).......................')
    // Object.keys(o)
    console.dir(o)
    console.dir('ssss' + o)
    o.x = 'xtttt'
    console.dir(o)
    console.dir('sssfff : ' + o)
    console.dir('console.dir(o).......................')
    
    o.assign(new Number(5));
    console.dir('console.dir(o).......................')
    console.dir(o)
    console.dir('ssss' + o)
    console.dir(3 + o)
    console.dir('console.dir(o).......................')
    // throw 'done'


    o.assign(new Boolean(true));
    console.dir('console.dir(o).......................')
    console.dir('ssss' + true)
    
    console.dir(o)
    console.dir('ssss' + o)
    console.dir(3 + o)
    console.dir(3 + true)
    console.dir('console.dir(o).......................')

    
// throw 'here'
    o = js.Versioned.create(o) // Doesn't work.
    var x = { a: 5 }
    o.vV = x;

    console.dir(o.vV)
    console.dir('console.dir(o).......................')
    console.dir(o)
    console.dir('console.dir(o).......................')
    console.dir(o.vV)
    // console.log("" + (o.vV.current=== x))
    console.log(assert.deepEqual(o.vV, x));
    // console.log(assert.strictEqual(o.vV, x, 'o being a Versioned o.vV and { a : 5 } found not strict equal'));
    console.log(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, x); // Strictequal will not match coz we get a proxy...
    // If we expect this to match use x = js.Versioned.create(x) which will be the proxy that will allways match.
    // PB : TODO -- We don't necessarily need to convert every property in x to a versioned property...
    assert.strictEqual(o.vV.current, x), 'but is not';


    o = js.Versioned.create()
    x = { a: 5 }
    o.vV = x;

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



var ten = { g : "10" }
// proxiedDescriptor.ten = ten;
var o1 = js.Versioned.create();
// Object.defineProperty(o1, 'vV', proxiedDescriptor.create(o1, 'vV', null, 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))')
// console.dir((ten == (+o1.vV)))  // false Doesn't work. Throws exception when valueOf and toString return non primitives.
console.dir((ten == o1.vV))
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)')
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);

// Test descriptor
var c = proxiedDescriptor.create(o1, 'dD', null, proxiedDescriptor.current);
Object.defineProperty(o1, 'dD', c);  
var dD = Object.getOwnPropertyDescriptor(o1, "dD");
console.dir('console.dir(c === dD)')
console.dir(c === dD)
console.log((dD.get === c.get) + ' ' + 'dD.get === c.get')


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


// Direct access test...
var vV = o1.vV;

vV.revision = { "12" : 12 }
console.dir(vV);
console.dir(vV.composite);
assert.deepEqual(vV, o1.vV );

var v = ['a', 'b', 'c'];  
var b = v.valueOf()
console.log(v===b)
console.dir(b)

// Deref Tests.
var proxT = { a : 'A', b : { bv : 'bv'}, c : ['e', 4, { x : 'x'}]}
var prox = js.mutableProxy.create(proxT)
prox = js.mutableProxy.create(prox)
prox = js.mutableProxy.create(prox)
console.dir(prox)
console.dir(proxT === prox)
prox = js.mutableProxy.create(prox)
prox = js.mutableProxy.create(prox)
prox = js.mutableProxy.create(prox)
console.dir(js.dereference(prox))
console.dir(proxT === js.dereference(prox))


elxr = utils.elxr
// var bitsToset = 0
// for(var i=0; i<32; i++) {bitsToset = js.setBit(i, bitsToset)}
// console.dir('' + elxr.toBitString(bitsToset) + ' (' + elxr.toBitString(bitsToset).length + ')')

// var unusedmasks = []
// console.dir(unusedmasks.pop())

// elxr.generateBitMaskedSequence([4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29])
// elxr.insertBits(16, [1,2])

console.dir(elxr.bitIndices)
console.dir(elxr.toBitString(elxr.bitIndices[31]))
console.dir(elxr.toBitString(elxr.insertBitsForMask(16, 6)))

var nwM = elxr.nextWithMask(6)
console.dir(elxr.toBitString(nwM.next().value))
console.dir(elxr.toBitString(nwM.next().value))
console.dir(elxr.toBitString(nwM.next().value))
console.dir(elxr.toBitString(nwM.next().value))