var utils = require("../bbhverse-pb-wip/utils")
var js = utils.js;

function asyncDynamicTaskStream(opts){

  const design = {
    create(){
      var conceptspace = 0; // 32 bit to start with.
      var next = 1;
      var all = 0;
      var segments = [];
      var __concepts = {};
      var __entagledRefs = new WeakMap();

      function assert(concept, multi){ return __concepts[concept].spin ? !(concept.id & multi) : (concept.id & multi) }

      function define_exclusions(exclusions, tangle){
        // Note : Do not store references to concept.entanglement as this is switched on update.
        if(exclusions.length < 2) throw 'Why would we call define_exclusions on a single or no item ? At the least 2 items are needed for mutual exclusion.'
        var results = [];
        // if any 2 concepts were setup as independent concpts they can later be entangled as mutual exclusion here.
        var entanglement = { unsync : 0 }; // Set of entagled bits including self.
        var entaglements = [];
        var maskForSet = 0; 
        __entagledRefs.set(entanglement, entaglements);
        var __entangledFn = (exclusion)=>{  // entaglement object has been fixed for all => collapse all previous.
          entanglement.unsync |= exclusion.entanglement.unsync | exclusion.id ;
          var collapsibles = __entagledRefs.get(exclusion.entanglement);
          if(collapsibles) { collapsibles.forEach(collapsible => { collapsible.entanglement = entanglement; }) }
          __entagledRefs.delete(exclusion.entanglement);
          exclusion.entanglement = entanglement;
          entaglements.push(exclusion);
        }
        var entangledFn = (exclusion)=>{ // entaglement object has not yet been fixed => replace.
          exclusion.entanglement.unsync |= entanglement.unsync | exclusion.id ;
          var collapsibles = __entagledRefs.get(entanglement);
          if(collapsibles){ collapsibles.forEach(collapsible => { collapsible.entanglement = exclusion.entanglement; }) }
          __entagledRefs.delete(entanglement);
          entanglement = exclusion.entanglement;
          entaglements = __entagledRefs.get(entanglement);
          entangledFn = __entangledFn;
        }
        var __tangle = Object.assign({}, tangle);
        // __tangle.unsync = __tangle.unsync & ~__tangle.insync;
        // __tangle.insync |= __concept.results[0].id;
        for(var i = 0; i < exclusions.length; i++) {
          var innerexclusions = __concepts[exclusions[i]] ? __concepts[exclusions[i]] : define(exclusions[i], __tangle);
          __tangle.unsync |= innerexclusions.results[0].id //| innerexclusions.results[0].entanglement.insync; 
          innerexclusions.results.forEach(exclusion=>{
            (exclusion.entanglement) ? entangledFn(exclusion)  // previously entangled.
            : (()=>{
                entanglement.unsync |= exclusion.id;
                exclusion.entanglement = entanglement;
                entaglements.push(exclusion)
              })() 
            results.push(exclusion)
            maskForSet |= exclusion.id;
          });
        }
        return { results : results, maskForSet : maskForSet} ;
      }
      function define_string(concept, tangle) { 
        all |= next;
        __concepts[concept] = { id : next, entanglement : Object.assign({}, tangle) }; // self is included in entanglement.
        __entagledRefs.set(__concepts[concept].entanglement, [__concepts[concept]] )
        next = next << 1; 
        return { results : [__concepts[concept]], maskForSet : __concepts[concept].id };
      }
      function define_array(concepts, tangle) {
        if(concepts.length > 1) return define_exclusions(concepts, tangle);
        else return define(concepts[0], tangle)
      }
      function define_object(concepts, tangle){
        var results = [];
        var maskForSet = 0;
        tangle = tangle || {};
        Object.keys(concepts).forEach(concept => {
          var __concept = define(concept, tangle);
          results.push.apply(results, __concept.results)
          var __tangle = Object.assign({}, __concept.results[0].entanglement);
          __tangle.insync |= __concept.results[0].id | tangle.insync;
          __tangle.unsync |= tangle.unsync;
          var isconcept = define( concepts[concept][0], __tangle ) 

          __tangle = Object.assign({}, __concept.results[0].entanglement);
          __tangle.unsync |= tangle.unsync | __concept.results[0].id;

          var notconcept = define( concepts[concept][1], __tangle )
          if(notconcept && notconcept.results) {
            __concept.results[0].entanglement.unsync |= notconcept.maskForSet;
            maskForSet |= __concept.maskForSet | notconcept.maskForSet  ;
          }
          if(isconcept && isconcept.results) {
            __concept.results[0].entanglement.insync |= isconcept.maskForSet;
            maskForSet |= __concept.maskForSet | isconcept.maskForSet ;
          }
        });
        results[0].entanglement.insync = maskForSet;
        return { results : results, maskForSet : maskForSet };
      }

      const conceptvariants = Object.assign(utils.__TPL__typeHandlers, {
          '[object String]' : define_string
        , '[object Array]' : define_array
        , '[object Object]' : define_object     
        , '[object Undefined]' : ()=>{} // Ignore             
      })
      function define(concept, tangle){
        return conceptvariants[Object.prototype.toString.call(concept)](concept, tangle);
      }
      function reveal(){ console.dir(__concepts) }

      return { define, reveal  }
    }
  }

  const coexstates = {
    create(){
      var conceptspace = 0; // 32 bit to start with.
      var next = 1;
      var all = 0;
      var segments = [];
      var __concepts = {};
      var __entagledRefs = new WeakMap();
      var __tango = { // JS : Need a hash with integer keys for performance
        // Eg:    
          // number matches key as a mask 
            // all bits in assertions bits get turned on and negations are turned off.    
          // number doesn't match key as a mask 
            // independents ? or 
            // all bits in assertions bits get turned off and negations are turned on.    
        // 0b1011110 : [ 
          //   0b1010000 // assertions : bits that are in sync. 
          // , 0b0001110 // negations  : bits that are off sync.
        // ]

        
      } 
      // Tango Entaglement internal model Specs - These are  permissable constraints for 2^n bit variations.
      // Key = any concept bit
      var t = {  0b00100000 /* a concept bit  */ : [
              /* concept bit is on - entangled coexistence with the state of concept bit.. */ 
              [ 
                  // all these bits must be turned off i.e these bits cannot co exist when the primary bit is turned on.
                  /* cannot coexit */ 0b00100110  // index 0 => 0 bits in this mask can coexit except for one bit which is the main concept bit which is included in this mask.
                  // Group constraints are not relevant here so not an array.

                ,  /* any one bit within a group can coexit */ [ 0b00101001 /*, ...othergroups Eg: 0b11000000*/ ]
                // each item in the array represents a constrained group of bits. 
                // index 1 => 1 bit in this mask can coexit with the main concept bit which is included in this mask. 
                // /*any n bits can coexit */ // index n => n bits in this mask can coexit with the main concept bit which is included in this mask. 
                // index i=1 to n each have 2^i variations each. At this level we have abstract nondeterministic view until all the bits computation results fix and reduce the result domain. 
                // Until such time they remain potential possibilities and which specific bits are on is ambiguious.

                   // all these bits must be turned on.
                ,  /*all these bits have to exist with this state (must coexit)*/ 0b00110000
                // can be collapsed to 1 single group.
              ]
            
              // independent bits where there is no entanglement are not stored. There can be an infinite or a large number of independent bits.
            , /* concept bit is off */
              [
                  0 // all these bits must be turned off.
                
                  // one or more permissible
                , [0] // Index 1 - any one is permissible from the none can coexit category since main bit is off. 
                // /*index (1 -> n) =>any n bits may be turned on in opposition */

                   //all these bits must be turned on.
                ,  /*all these bits have to exist with this state in opposition*/  0b11000000
              ]
          ] 
      }
      (()=>{
        var cannotCoexist = 0;
        for(var i = 1; i < t[0b00100000][0].length; i++) { cannotCoexist |= t[0b00100000][0][i] } 
        t[0b00100000][1][0] |= cannotCoexist;
      })()
      (()=>{
        var cannotCoexist = 0;
        for(var i = 1; i < t[0b00100000][1].length; i++) { cannotCoexist |= t[0b00100000][1][i] } 
        t[0b00100000][0][0] |= cannotCoexist;
      })()
      t[0b00100000][1][1] |= t[0b00100000][0][0]  

      function define_exclusions(exclusions, tangle){
        // Note : Do not store references to concept.entanglement as this is switched on update.
        if(exclusions.length < 2) throw 'Why would we call define_exclusions on a single or no item ? At the least 2 items are needed for mutual exclusion.'
        var results = [];
        // if any 2 concepts were setup as independent concpts they can later be entangled as mutual exclusion here.
        var entanglement = { unsync : 0 }; // Set of entagled bits including self.
        var entaglements = [];
        var maskForSet = 0; 
        __entagledRefs.set(entanglement, entaglements);
        var __entangledFn = (exclusion)=>{  // entaglement object has been fixed for all => collapse all previous.
          entanglement.unsync |= exclusion.entanglement.unsync | exclusion.id ;
          var collapsibles = __entagledRefs.get(exclusion.entanglement);
          if(collapsibles) { collapsibles.forEach(collapsible => { collapsible.entanglement = entanglement; }) }
          __entagledRefs.delete(exclusion.entanglement);
          exclusion.entanglement = entanglement;
          entaglements.push(exclusion);
        }
        var entangledFn = (exclusion)=>{ // entaglement object has not yet been fixed => replace.
          exclusion.entanglement.unsync |= entanglement.unsync | exclusion.id ;
          var collapsibles = __entagledRefs.get(entanglement);
          if(collapsibles){ collapsibles.forEach(collapsible => { collapsible.entanglement = exclusion.entanglement; }) }
          __entagledRefs.delete(entanglement);
          entanglement = exclusion.entanglement;
          entaglements = __entagledRefs.get(entanglement);
          entangledFn = __entangledFn;
        }
        var __tangle = Object.assign({}, tangle);
        // __tangle.unsync = __tangle.unsync & ~__tangle.insync;
        // __tangle.insync |= __concept.results[0].id;
        for(var i = 0; i < exclusions.length; i++) {
          var innerexclusions = __concepts[exclusions[i]] ? __concepts[exclusions[i]] : define(exclusions[i], __tangle);
          __tangle.unsync |= innerexclusions.results[0].id //| innerexclusions.results[0].entanglement.insync; 
          innerexclusions.results.forEach(exclusion=>{
            (exclusion.entanglement) ? entangledFn(exclusion)  // previously entangled.
            : (()=>{
                entanglement.unsync |= exclusion.id;
                exclusion.entanglement = entanglement;
                entaglements.push(exclusion)
              })() 
            results.push(exclusion)
            maskForSet |= exclusion.id;
          });
        }
        return { results : results, maskForSet : maskForSet} ;
      }
      function define_string(concept, containing) { 
        all |= next;
        __concepts[concept] = { id : next, ex : 0, in : 0, entanglement : Object.assign({}, tangle) }; // self is included in entanglement.
        next = next << 1; 
        return { results : [__concepts[concept]], maskForSet : __concepts[concept].id };
      }
      function define_array(concepts, tangle) {
        if(concepts.length > 1) return define_exclusions(concepts, tangle);
        else return define(concepts[0], tangle)
      }
      function define_object(concepts, tangle){
        var results = [];
        var maskForSet = 0;
        tangle = tangle || {};
        Object.keys(concepts).forEach(concept => {
          var __concept = define(concept, tangle);
          results.push.apply(results, __concept.results)
          var __tangle = Object.assign({}, __concept.results[0].entanglement);
          __tangle.insync |= __concept.results[0].id | tangle.insync;
          __tangle.unsync |= tangle.unsync;
          var isconcept = define( concepts[concept][0], __tangle ) 

          __tangle = Object.assign({}, __concept.results[0].entanglement);
          __tangle.unsync |= tangle.unsync | __concept.results[0].id;

          var notconcept = define( concepts[concept][1], __tangle )
          if(notconcept && notconcept.results) {
            __concept.results[0].entanglement.unsync |= notconcept.maskForSet;
            maskForSet |= __concept.maskForSet | notconcept.maskForSet  ;
          }
          if(isconcept && isconcept.results) {
            __concept.results[0].entanglement.insync |= isconcept.maskForSet;
            maskForSet |= __concept.maskForSet | isconcept.maskForSet ;
          }
        });
        results[0].entanglement.insync = maskForSet;
        return { results : results, maskForSet : maskForSet };
      }

      const conceptvariants = Object.assign(utils.__TPL__typeHandlers, {
          '[object String]' : define_string
        , '[object Array]' : define_array
        , '[object Object]' : define_object     
        , '[object Undefined]' : ()=>{} // Ignore             
      })
      function define(concept, tangle){
        return conceptvariants[Object.prototype.toString.call(concept)](concept, tangle);
      }
      function reveal(){ console.dir(__concepts) }

      return { define, reveal  }
    }
  }

  // 'created' is implied by existence.
  var states = {
    'running' : [
          /* assertions */ [ ] 
        , /* negations = not running */ [ { 
              'unstarted' : [[ /* unstarted */]
            , /* not unstarted = started at some point */ [
                  'paused'  // can be resumed from interrupted (=incomplete or in progress)
                  // , sleeping -> ran a little and is now paused same as parent state.
                  // , waiting on other things
                  // ... sleeping, interrupted ...
                , { 'stopped' : [[ // unlike paused cannot be resumed can be restarted i.e rerun from the beginning.
                      'succeded' // - completed -> process completed  
                    , 'failed'   // - Either processing failed or prematurely exited with exception -> with an error. Non exception cases may have run to completion and failed.
                    , 'aborted'  // - terminated -> was not allowd to completed. Cannot say succeded or failed. 
                                  // incomplete result ( neither success nor failure ) was stopped and abandoned befeore it could run to completion.
                  ], [] ]}
              ]
            ]} ]
    ]
    // unassociated independent co-existence to 'running'. Sibling level in an object.
  }

  var stateFactory = design.create();
  var x = stateFactory.define(states);
  stateFactory.reveal();

  // console.dir(JSON.stringify(x));
}


module.exports = asyncDynamicTaskStream;
asyncDynamicTaskStream();