var scripthostName = 'node'
var __Promise = {};
var promises = [];
function startPromises(){
  promises.forEach(function(p){ 
    // console.log(p.chain)
    p ? p() : null; 
    // promises.splice(0,1) 
  })
}

function isWin(){ return /^win/.test(process.platform) }
var stampedFilePfx = function(date) {
  return date.getFullYear() +
  ('0' + (date.getMonth() + 1)).slice(-2) + 
  ('0' + date.getDate()).slice(-2) +
  ('0' + date.getHours()).slice(-2) +
  ('0' + date.getMinutes()).slice(-2) +
  ('0' + date.getSeconds()).slice(-2);
}
var runtimestamp = (new Date()).getTime();

try {
  console.log("detected node")
  // var WScript = console || WScript // Dummy
  // console.Echo = console.log

  __Promise = Promise 
    var __require = require
    var map = Array.map

    function startPromises(){
      promises.forEach(function(p){ 
        // console.log(p.chain)
        p ? Promise.resolve(p) === p ? p : p() : null; 
        // promises.splice(0,1) 
      })
    }
    
      
    // --------------------------------------------
    // Node Exists. Lets launch ourselves in Node itself 
  
    WScript = function(){}
    WScript.Echo = function(m){
      console.log('Invalid Wscript')
      throw "Failed in Node Run."
    }
    var wait = function(ms, cb) {any 
        return new __Promise(function(resolve){ setTimeout(resolve, ms) } ).then(function(){ cb() });
      }
      var fs = require('fs')
      var existsSync = require('fs').existsSync;
      var existsSyncFolder = existsSync
      var path = require('path');
      console.log('before cliverse')
      var cli = require('./cliverse')
      var nodeShellExec = cli.nodeShellExec;
      console.log('before bbhverse')
      var utils = require('bbhverse');


      var any = utils.any;
      var wait = setTimeout

    // --------------------------------------------

    // PB : TODO -- This should be parent dir if elxr is already installed.
    var selectedinstance = { root : path.resolve(".") } 
    __main(selectedinstance)



}
catch(e) {
  WScript.Echo('detected wsh')
  scripthostName = 'wsh'
  WScript.Echo(WScript.FullName)

  // WScript.Echo('--' + WScript.Arguments(0) + '--')
  // WScript.Echo('------------ALL-----------')
  // var argEnumerator = new Enumerator(WScript.Arguments)
  // for(; !argEnumerator.atEnd(); argEnumerator.moveNext()){
  //     WScript.Echo('-arg-' + argEnumerator.item() + '-arg-')    
  // }


  // // Unnamed
  // WScript.Echo('------------UNNAMED-----------')
  // argEnumerator = new Enumerator(WScript.Arguments.Unnamed)
  // for(; !argEnumerator.atEnd(); argEnumerator.moveNext()){
  //     WScript.Echo('-arg-' + argEnumerator.item() + '-arg-')    // Value
  // }

  // // Named
  // WScript.Echo('------------NAMED-----------')
  // argEnumerator = new Enumerator(WScript.Arguments.Named)
  // for(; !argEnumerator.atEnd(); argEnumerator.moveNext()){
  //     // WScript.Echo('-arg-' + argEnumerator.item(argEnumerator) + '-arg-')   // Key
  //     WScript.Echo('-key-' + argEnumerator.item() + '-key-')    // Key
  //     WScript.Echo('-val=' + WScript.Arguments.Named(argEnumerator.item()) + '=val-')    // Value
  // }

  // cscript elxr/i.win.js /all:true
  var all = WScript.Arguments.Named('all');

  trim = function(str) { return str.replace(/^\s+|\s+$/g, ''); };

  var fs = new ActiveXObject('Scripting.FileSystemObject');
  var WshShell = WScript.CreateObject("WScript.Shell")
  var ENV = WshShell.Environment("Process")
  var sfn = WScript.ScriptFullName
  // WScript.Echo(sfn)
  var cd = fs.GetAbsolutePathName(".")
  var cfn = cd + '\\i.win.js'
  // WScript.Echo(cfn)
  var waslaunchedwithenv = trim(ENV.Item('LAUNCHEDWITHENV'));

  // for(e=new Enumerator(ENV); !e.atEnd(); e.moveNext()) WScript.echo(e.item(e));
  if(waslaunchedwithenv !== "YES"){

    var a = fs.CreateTextFile("launchwithenv.bat", true)
    a.WriteLine("@echo off")
    a.WriteLine("echo %PATH%")
    var gitpath = "C:\\Program Files\\Git\\cmd"
    var nodepath = "C:\\Program Files\\nodejs\\"
    var codepath = "C:\\Users\\Pradeep\\AppData\\Local\\Programs\\Microsoft VS Code\\bin"
    var python2path = "C:\\Python27" // Python installer doesnt write to PATH!!!
    a.WriteLine("set PATH=%PATH%;" + gitpath )
    a.WriteLine("set PATH=%PATH%;" + nodepath )
    a.WriteLine("set PATH=%PATH%;" + codepath )
    a.WriteLine("set PATH=%PATH%;" + python2path )
    // a.WriteLine("set LAUNCHEDWITHENV=YES" )
    
      var __ALIAS__STAMP__ = '9e7bebe0-1f57-11ec-8f88-778ffeea9d1b'  
    var currentIsExlr = function() {
      if(fs.FileExists('./' + __ALIAS__STAMP__)) return true
    }
    var runningInExlr = currentIsExlr()
    if(cfn === sfn && runningInExlr) {
        a.WriteLine("cd ..");
        a.WriteLine("SET LAUNCHEDWITHENV=YES && cscript "+cfn+" /all:true") // PB : TODO -- Retain all script args...
    }
    else {
      var guesselxr = fs.FileExists(cd + './elxr/' + __ALIAS__STAMP__)
      if(guesselxr) {
        a.WriteLine("SET LAUNCHEDWITHENV=YES && cscript "+cd + './elxr/' + 'i.win.js' +" /all:true")
      }
      else a.WriteLine("SET LAUNCHEDWITHENV=YES && cscript "+sfn+" /all:true")    
    }
   
    // a.WriteLine("powershell.exe ^")
    // a.WriteLine("[Environment]::GetEnvironmentVariable('Path')" )
    a.Close()
    WshShell.run("cmd /k launchwithenv.bat")
    WScript.Quit()
    // WshShell.run("powershell -noexit [Environment]::GetEnvironmentVariable(\"\"Path\"\"\) ")
    // WshShell.run("powershell -noexit [Environment]::SetEnvironmentVariable(\"\"Path\"\", $env:Path + \"\";C:\\tttt\"\", \"\"Machine\"\"\) ")
  }
  
  console = { 
      log : function(m) { WScript.Echo(m)}
    , error : function(m) {WScript.Echo(m) } 
    , dir : function(o) {
      for(var i in o){ console.log(i + ' : ' + o[i])}
    }
  }

  // if(!String.prototype.trim) String.prototype.trim = function(){
  //   return this.replace(/^\s+|\s+$/g, '');
  // };

  // var ovrrides = {
  //   resolve : function(v){
  //     if(v && v.then) return v;
  //     var p = new Promise(function(resolve, reject){  resolve(v) });
  //     return p;
  //   }
  // };

    // // --------------------------------------------
    // // Cscript
    var wsh = true;
    function isWin(){ return true; }
    // If UCase( Right( WScript.FullName, 12 ) ) = "\CSCRIPT.EXE" Then
  
    var clii = {
      question : function(q, answercb){
        WScript.Echo(q)
        // console.log('WScript.StdIn : ' + WScript.StdIn.ReadLine() + ' ==== ')
        var answer = WScript.StdIn.ReadLine()
        answercb(answer)
      }
    }
    var prompter = { 
        ask : function(q){
          // Needs to be serialized. Parallel asks are not possible.
          // const clii = readline.createInterface({ input: process.stdin, output: process.stdout });
          
          
          return new Promise(function(resolve, reject){
            clii.question(q, function(answer){ 
              // clii.close();
              // console.log("resolve is being called");
              resolve(answer) 
            })
          })
        }
      }
  
  
  
    var cli = {
      prompt : function(choices, label, defaultchoice){
        var options = [];
        // console.dir(choices)
        choices.forEach = forEach
        choices.forEach(function(choice, i, a){ 
          // console.log(choice)
          // console.log('------------------------')
          // console.log(  ((+i) + 1) + ' ' +  choice )
          options.push(  ((+i) + 1) + ' ' +  choice )
          // console.log('------------------------')        
        })
        // console.log(options)
        return prompter.ask( label + '\n' + options.join('\n') + '\n  default ( <= ' + (defaultchoice || choices[0]) + ' ) : ' 
          ).then( function(choice){
            if(!choice) return defaultchoice || choices[0];
            if(choice && isNaN(+choice)) return choice;
            return choices[(+choice) - 1];
          })
      } 
    }
  
    var any = function(iterable, continueOnFailure) {
      // var cancelsignal = Symbol()
      if(!iterable.reduce) iterable.reduce = reduce
      // console.log('iterable.reduce ' + iterable.reduce)
      var cancelsignal = "cancelme{mangledrunid}"
      return iterable.reduce( 
          function(p, tasq, i ,a) {
            // console.dir(a)
            // console.log('Entered')
              var handleError = function(err, pVal){
                if(err !== cancelsignal) {
                  // Cancel only once on first failure.
                  console.warn('Possible failure for task with result : ' )
                  console.log('Failed : ' + err.message + ' ')
                  console.dir(pVal)
                  console.dir(p)
                  if(i>0 && a[i-1].info) console.dir(a[i-1].info)
                  console.error('Error : ' + err.stack)
                  console.error(a[i-1])
                  a[i-1] ? console.log("tasq : " + a[i-1].toString()) : null;
                  if(!continueOnFailure) {console.log("Cancelling remaining on any one failure ..."); throw cancelsignal}
                  else return pVal;
                  // tasq ? console.log("tasq : " + tasq.toString()) : null; // Previous task is what failed not the one we are going to run.
                }
              }
    
              if(Promise.resolve(p) === p ) {
                if(i>0 && a[i-1].info) p.info = a[i-1].info;
                console.dir(' p.then is ' + p.then)
                return p.then( function(pVal){
                  // Falsy values are no longer treated as task failure exceptions. Specially for Promises. 
                  // Even tasq function wrappers are required to return promises that eventually either resolve or reject..
                  // Failures are known for promises on reject.
                  // In future if we support direct sync function execution with a result examination for failure 
                  // we could examine the result of the function as falsy's... or a result evaluator handler needs to be passed in... 
                  // if(!pVal) handleError({ error : true, message : 'Failed without result' }, pVal)
    
                  // Truthy values are failures if obj has error=true.
                  if(pVal && pVal.error) handleError(pVal, pVal)
    
                  var trycall = function(tasq){
                    try { 
                      var result = tasq() // PB : TODO -- Handle scope for call 
                      if(tasq.resultHandler) return tasq.resultHandler(result)
                      // else (Promise.resolve(result) === result ) ? result.start() : null
                      return result 
                    } catch (error) {
                      console.error(error);
                      console.error('Error : ' + error ? error.stack : 'No stack')
                      if(!continueOnFailure) throw error; // PB : TODO -- Support array of results for any with or without continueonfailure.
                    }
                  }
                  
                  var handleNext = function(){
                    // console.log('Task finished with result : ')
                    // console.dir(pVal)
                    if(i>0 && a[i-1].info) console.dir(a[i-1].info)
                    if(!tasq && !continueOnFailure) { console.log('Error : No task specified.'); throw false;}
                    else if(!tasq) { console.log('Error : No task specified.'); return false;}
                    return (Promise.resolve(tasq) === tasq ) ? tasq /*.start()*/ : trycall(tasq) ;
                  }
    
                  if(Promise.resolve(pVal) === pVal) {
                    // Passed in function retured a promise. We still need to wait for it.
                    pVal.then(function(pVal){ return handleNext(); })
                  }
                  else return handleNext()
                    
                })['catch'](function(error) {
                  if(error !== cancelsignal) {
                    console.log('E3 : i = ' + i);
                    if(error.result) console.error(error.result)
                    console.error('Error : ' + (error.message || error.messages))
                    console.error('Error : ' + error.stack)
                    tasq ? console.log("tasq : " + tasq.toString()) : null;
                    console.log('debugData 3-------------------------');
                    // handleError()
                    throw error
                  }
                  else throw cancelsignal;
                })  
              }
              else if(!p) {
                handleError({ error : true, message : 'Failed without result' }, pVal)
                console.log("Bypass remaining on prior failure");
                return false; // All remaining tasks will return false in the any results even if they are promisies still running or functions not initiated.
              }
              else return p; // A truthy value 
          }
          , Promise.resolve(true)
      );
    }
   
  
    // __Promise = ovrrides 
    // PB : TODO -- iterators are messed up in cscript without enumerables...
    function forEach(eachFn, o){
      // console.dir(this)
      // console.log('------------------------------------')
      // console.log(''+ {})
      // console.log('------------------------------------')
      if('' + this === '[object Object]') {
        // console.log('' + this)
        // console.dir(this)
        // var o = this
        // for(var i in o){ console.log(i + ' : ' + o[i])}
        for(var i in this){
          if(i === 'forEach') continue;  // Enumerable in cscript not supported ??? 
          // console.log(i) 
          eachFn(this[i], i, this) 
        }
      }
      else { 
        for(var i=0; i<this.length; i++) eachFn(this[i], i, this) 
      }
    }
    function reduce(reducnFn, iv){
      var acc = iv
      for(var i=0; i<this.length; i++) {acc = reducnFn(acc, this[i], i, this) }
      return acc;
    }
  
    function map(eachFn){
      var mapped = []
      for(var i=0; i<this.length; i++) mapped.push(eachFn(this[i]))
      return mapped
    }
  
    var wait = function(cb, ms) { WScript.Sleep(ms); cb() }
    var fso = new ActiveXObject('Scripting.FileSystemObject');
    var existsSync = function(filepath){ return fso.FileExists(filepath) }
    var fs = {
      writeFileSync : function(filepath, text) {
        // console.log('------------------writeFileSync----------------------')
        // console.log(filepath)    
        // console.log('------------------writeFileSync----------------------')
        
        var fh = fso.CreateTextFile(filepath, true); 
        fh.WriteLine(text); 
        fh.Close(); 
      }
      
      , mkdirSync : function(path) {
        fso.CreateFolder(path)
      }
      , readFileSync : function(filepath){
        var objFileToRead = fso.OpenTextFile(filepath,1)
        var strFileText = objFileToRead.ReadAll()
        objFileToRead.Close()
        return strFileText    
      }
      , unlinkSync : function(filepath){ fso.DeleteFile(filepath) }
    }
  
    var path = {
      resolve : function(path){ return fso.GetAbsolutePathName(path) }
        , dirname : function(filepath) {
          var normalized = this.normalize(filepath)
          var li = normalized.lastIndexOf("\\")
          if( li > -1) {
            return normalized.substring(0, li)
          }
        }
        , normalize : function(path){ 
          return path.replace(/\//g,'\\');
        }
    }
  
    // Detect or specify install directory.
    var selectedinstance = { root : path.resolve(".") }
  
    var existsSyncFolder = function(path){
          return fso.FolderExists(path)
        }
    
    var shell = new ActiveXObject('shell.application'); 
  
    promises.forEach = forEach
    function startPromises(){
      promises.forEach(function(p){ 
        // console.log(p.chain)
        p.start(); 
        // promises.splice(0,1) 
      })
    }
 
    function batchshellescape(str) {
        return str.replace('=', '^=') 
    }
  
    function nodeShellExec(command, cargs, options){
  
      // for(var arg=0; arg < cargs.length; arg++) {
      // 	// cargs[arg] = '"' + batchshellescape(cargs[arg]) +'"';
      //   cargs[arg] = '"' + cargs[arg] +'"';
      // }

      var elevatedshellexecute = function(cmd, argstr){
        shell.ShellExecute(cmd, argstr , "", "", 1);
      }
  
      var shellExec = function(cmd, argstr){
        
        var objShell = WScript.createobject("wscript.shell")
        // console.log(argstr.join( ' '))
	      console.log(cmd + ' ' + argstr.join(' '))
        var oExec = objShell.Exec(cmd + ' ' + argstr.join(' '))
        var result = {}
        var shellresult = { shell : objShell, result : result }
  
        var WshRunning = 0
        var WshFinished = 1
        var WshFailed = 2
        while(oExec.Status === WshRunning){
          WScript.StdOut.write('s.')
          WScript.Sleep(500)
        }
        var strOutput = '\n'
        switch(oExec.Status) {
          case WshFinished : 
            strOutput = oExec.StdOut.ReadAll() 
            result.success = true;
            result.code = 0
            break;
          case WshFailed : 
            strOutput = oExec.StdErr.ReadAll() 
            result.success = false;
            result.code = WshFailed
            break;
          default : strOutput = 'failed' 
          break;
        }
        result.result = command + ' ' + cargs + ' exited with code ' + result.code
        result.messages = [strOutput]
        // console.log(strOutput)
        // WScript.Echo(oExec.Status)
        // WScript.Echo(oExec.ProcessID)
        // WScript.Echo(oExec.ExitCode)
  
        // console.log(objShell.StdOut.ReadAll)
        // console.log(objShell.StdErr.ReadAll)
        // objShell = WScript.createobject("wscript.shell")
        objShell = null;
        return shellresult;
      }
  
      var p = null;
      var pworker = function(resolve, reject){
        // console.dir(p)
        var pfx = selectedinstance.root + '\\.elxr\\run-' + runtimestamp + '\\' + stampedFilePfx(new Date())
        // console.log('p.chain.length ================ ' +  p.chain.length)
        options = options || {
          runFile : path.normalize( pfx + "out.txt")
          // runFile : null
        }
        var runFile = null;
        var runFile = options.runFile || pfx + command + cargs + "out.txt";
        // console.log(runFile)
        var args = cargs.concat() 
        // runFile ? (args.push(">"), args.push(runFile)) : cargs 
        // console.log(command + ' ' + args.join(' '))
  
        // command = 'cmd'
        // args = ['/c', 'start',
        //   '/WAIT', selectedinstance.root + '/Downloads' + '/' + 'Git-2.33.0.2-64-bit.exe'
        //   , '/VERYSILENT'
        //   // , '/MERGETASKS=!runcode' // This is required only for vscode...
        // ]
  
  
        var runbat = path.normalize(pfx + "run.bat")
        // console.log('runbat : ' + runbat)
        fs.writeFileSync(runbat, 
          '@echo off  \r\n' +
          (options.cwd ? 'cd ' + options.cwd + '  \r\n' : '') + 
          // ' cmd /k notepad.exe \r\n' +  
          command + ' ' + args.join(' ') + ' \r\n' +
          'cmd /c echo done >> ' + runFile  + ' \r\n' +
          'echo done'
        )
        // fs.writeFileSync(runFile, 'started')
        // WScript.Quit()
        // elevatedshellexecute(runFile)
        // while(!existsSync(runbat)) { wait(function(){
        //   console.log('awiting batch : ' + runbat)
        //   // shellExec( 'start', ['/W', '/b', runbat])
        //   // shellExec( 'cmd', ['/k', '/b', runbat])
        //   shellExec( command, args)
        //   // cmd /b /c
        // }, 500) }
  
        
        console.log(options.waitmsg || ('awaiting ' + command + ' ' + args.join(' ')))
        var shellresult = shellExec( 'cmd', [ '/c', runbat])
        // var shellresult = shellExec( 'cmd', ['/c', runbat])
        // var shellresult = shellExec( command, args)
        
   
        
        var wrapup = function(result) {
          // console.log('Wrapping up')
          try {
            // console.log('resolving.....................'); 
            // console.log('--------------P' + result.messages.join(' ').trim() +'P--------------')
            // if(result.messages.join().trim()) resolve(result)
            // else reject(result)
            resolve(result)
          }
          catch(e){
            // console.dir(e) 
            if(e.message === 'Input past end of file') {
              // console.log('---------------------------------------')
              // console.dir(result)
              resolve(result)
            }
            else {
              // console.dir(e)
              reject(e)
            }
          }
        }
  
        // if(runFile){
        //   var waitr = function(){
        //     WScript.StdOut.write('w.')
        //     if(existsSync(runFile)) {
        //       var strOutput = fs.readFileSync(runFile)
        //       shellresult.result.messages = [strOutput]
        //       wrapup(shellresult.result)
        //       fs.unlinkSync(runFile)
        //     }
        //     // console.dir(shellresult.result)
        //     wait( waitr, 500)
        //   }
        //   // console.log(wait)
          
        //   wait( waitr, 500)
        // }
        // else {
          // console.log('There is no runfile.')
          console.log(shellresult.strOutput)
          
          wrapup(shellresult.result)
        // }
      }
      p = new Promise(pworker)
      // promises.push(p)
      // // Promise tThens don't get hooked up... we need to postpone promise starting until all the thens are registered...
      // wait(function(){ 
      //   console.log('p.chain.length' + p.chain.length)
      //   p.start() } , 5000) // Bind and setTImeout is not supported by JScript
  
      return p;
    } 
    createPromiseClass(/*ovrrides*/)
    __main( selectedinstance )
  
  
  function createPromiseClass(overrides) {
  
    function reject(e){ 
      var p = this;
      console.log('Promise Rejection : ' + p.fn)
      console.dir(e)
      // throw e;f
      if(p.state !== PromiseClass.PENDING) { console.error ('Error : Promise Rejection can only be called once')}
      var __i = 0;
      var __e = e;
      do {
        for(var i = __i; i < p.chain.length; i++, __i = i) if(p.chain[i].isCatch) break;
        try {
          for(var i = __i; i < p.chain.length; i++, __i = i) if(p.chain[i].isCatch) { p.chain[i](__e); break; } 
          __i++;
          __e = null;
        }
        catch(e){ __i++; __e = e}
      } while(__e)
      do {
        try { for(var i = __i; i < p.chain.length; i++, __i = i) if(!p.chain[i].isCatch) { p.result = p.chain[i](p.result); } }
        catch(e){ 
          __i ++;
          do {
            try {
              for(var i = __i; i < p.chain.length; i++, __i = i) if(p.chain[i].isCatch) { p.chain[i](__e); break; } 
              __i ++;
              __e = null;
            }
            catch(e){ __i++; __e = e}
          } while(__e)
        } 
      } while ( __i < p.chain.length )
      p.state = PromiseClass.REJECTED;
    }
  
    function resolve(result){   
      var p = this;
      // console.log(result + ' resolve was called with chain length ' + p.chain.length )
      if(p.state !== PromiseClass.PENDING) { console.error ('Error : Promise Resolve can only be called once')}
      p.result = result;
      if(PromiseClass.resolve(p.result) === p.result) {
        // console.log( 'result is still a promise waiting for result and promisechain' )
        waitForResult(p.result, function(r){ p.processchain(r) })
      }
      else {
        // console.log( 'result is value waiting for promisechain' )
        // console.dir(result)
        return p.processchain(result)
      }
    };
  
    function processchain(r){
        
      var __i = 0;
      var i = __i;
      var __e = null;
      p = this;
      // console.log('processchain chain.length : ' + p.chain.length)
  
      function __processchain(r){
  
        function waitForThen(p){  
          if(i < p.chain.length) {
            if(!p.chain[i].isCatch) { 
              // console.log('chain idx : ' + i + ' Executing : then ' + p.result + ' ' + p.chain[i])  
              try {
                p.result = p.chain[i](p.result)
                
                if(PromiseClass.resolve(p.result) === p.result) {
                  // console.log('chain idx : ' + i + ' result is still a promise starting it ')  
                  // console.dir(p.result.chain)
                  // console.log('p.result.start : ' + p.result.start + '    ------------------    ')
                  p.result.start()
                  // console.log(p.result.fn + '    ------------------    ')
                  waitForResult(p.result, function(r){
                    // console.log('we waited')
                    // WScript.write('.')
                    p.result = r; i++; __i = i;
                    waitForThen(p)
                  })
                }
                else {
                  i++; __i = i;
                  waitForThen(p)                          
                }
              }
              catch(e) { 
                i++; __i = i;
                __e = e;
                console.log('failed on index ' + __i + p.chain[__i-1] )
                console.dir(e)
                waitForCatch(p);
              }
            } 
            else {
              // console.log(i + ' Skipping catch : ' + p.result + ' ' + p.chain[i])  
              i++; __i = i;
              waitForThen(p)
            }
          }
          else return p.state = PromiseClass.FULFILLED;
        }
        function waitForCatch(p) {  
          if(i < p.chain.length) {
            if(p.chain[i].isCatch) { 
              console.log('chain idx : ' + i + ' Executing : catch : ' + p.result + ' ' + p.chain[i]) 
              try {
                p.result = p.chain[i](__e);
    
                if(PromiseClass.resolve(p.result) === p.result) {
                  p.result.start()
                  waitForResult(p.result, function(r){
                    p.result = r; i++; __i = i;
                  })
                  waitForThen(p)
                }
                else {
                  p.result = r; i++; __i = i;
                  waitForThen(p)
                }
              }
              catch(e){ i++; __i = i;; __e = e; waitForCatch(p) } 
            } 
            else {
              i++; __i = i;
              waitForCatch(p)
            }
          }
          else return p.state = PromiseClass.REJECTED
        }
        waitForThen(p);
      }
      __processchain(r)
    }
  
    var create = function(fn){
      var p = { 
        then : function(thenfn){
          // console.log('Adding then') 
          thenfn.isThen = true
          // if(Object.prototype.toString.call(p.chain) !== '[object Array]') console.dir(p.chain)
          p.chain.push(thenfn)
          return p;
        }
        , 'catch' : function(catchfn) {
          catchfn.isCatch = true
          p.chain.push(catchfn)
          return p;
        }
        , start : function(){
          if(this.started) {
            console.error('Cannot start more than once...')
            return p
          };
          this.started = true;
          this.fn = fn;
          try { fn( function(r){ 
            // console.log('calling presolve in starter ')
            p.resolve(r) } , function(r){ p.reject(r) } ) }
          catch(e){ 
            // console.log('start catch : ' + e)
            // console.log('p.chain.length : ' + p.chain.length)
            // // console.dir(p)
            // console.dir(e)
            // console.log(fn)
            p.reject(e) 
          }
          return p
        }
        , state : PromiseClass.PENDING
        , chain : []
        , reject : reject
        , resolve : resolve
        , processchain : processchain
      }
      p.chain.forEach = forEach;
      return p;
    }
  
    var PromiseClass = function(fn /*, donotstart */){
      var p = create(fn)   
      // if(!donotstart) {
      //   wait(function(){ 
      //     console.log('p.chain.length' + p.chain.length)
      //     p.start() } , 500) // Bind and setTImeout is not supported by JScript
      // }
      return p
    }
  
    PromiseClass.PENDING = 1
    PromiseClass.FULFILLED = 2
    PromiseClass.REJECTED = 3
    // PromiseClass.STARTED = 4
    PromiseClass.isSETTLED = function(p){ p.state === PromiseClass.FULFILLED || p.state === PromiseClass.REJECTED }
  
    PromiseClass.resolve = function(v){
      if(v && v.then) return v;
      var p = create(function(resolve, reject){
        resolve(v)
      });
      // console.log('p.chain in resolve. length ' + p.chain.length)
      // p.result = v;
      // wait(function(){ p.processchain(v) }, 0)
      return p;
    }
  
    function waitForResult(p, cb){
      if(!p) return cb(p)
      if(p.state !== PromiseClass.PENDING) cb(p.result) 
      // console.log(p.state + ' Waiting for ..... ' + p.runFile)
      if(p.runFile && false) {
        while(!existsSync(p.runFile) && p.state === PromiseClass.PENDING) { 
          console.log('Waiting for ResultFle'); wait(cb, 500) 
        }
        cb(p.result)
      }
      else {
        // while(p.state === PromiseClass.PENDING) {
          // console.log('Waiting for Result')
          function waiter(){
            // console.log(p.result)
            if(p.state === PromiseClass.PENDING) wait(waiter, 500);
            else return cb(p.result)            
          }
          wait(waiter, 500)
        // }
      }
    }
  
    PromiseClass.all = any; // Serialized...
  
    PromiseClass.__all = function(arr){
      arr.forEach = forEach;
      var resultPs = [];
      var results = [];
      console.log('All : ' + arr.length)
      var pAll = new PromiseClass(function(resolve, reject){
  
        console.log('All started : ' + pAll)
        // console.dir(pAll)
        
        arr.forEach(function(p){ 
          if(!p.then) { var pfn = p; 
            p = new PromiseClass(function(resolve, reject){ 
              try{
                resolve(pfn()) 
              }
              catch(e){
                reject(e)
              }
            })
            p.start() 
          }
          else { 
            // !p.start ? p.start = function(){return p} : null
            // p.start()
            // .then( function(){ waitForResult(p, function(r){ results.push(r) })  })
          }
          resultPs.push(p)
          // waitForResult(p, function(r){ results.push(r) })
        })
        
        // PB : TODO -- This is the same as processchain!!!
        var allwaitr = function(){
          var allResolved = true
          console.log('resultPs : ' + resultPs.length)
          for(var rIdx =0; rIdx < resultPs.length; rIdx++ ){
              
            if(resultPs[rIdx]) {
              allResolved = false
              waitForResult(resultPs[rIdx], function(r){ 
                if(Promise.resolve(r) !== r) results[rIdx] = r; resultPs[rIdx] = null; 
              })
              break;
            }
          }
          if(allResolved) {
            // console.log('All Reseloved')
            // console.dir(results)
            resolve(results)
          }
          else wait(allwaitr, 500)
        }
        wait(allwaitr, 500)
      })
      
      // pAll.chain = arr;
      return pAll
    }
    
    // PromiseClass.resolve = overrides.resolve;
    Promise = PromiseClass;
    return PromiseClass;
  }
}

function __main( selectedinstance ){
  var downloadsdir = selectedinstance.root + '\\..\\Downloads';  

  var callsheltask = function(args) { 
    // console.log('callsheltask : ' + args)
    return function() { return nodeShellExec.apply(null, args) } }
  
  var instanceName = 'elixir' 
  var reposerver = 'https://git.bbh.org.in';
  var gitUser = 'guest';
  var gitpassword = 'guest'; 
  var gitEmail = 'guest@bbh.org.in';
  var gitPreinstalled = true;


  var BUILD_VERSION = '[VI]Version: {version} - built on {date}[/VI]';
  function getVersion() { return BUILD_VERSION; }
  console.log(getVersion())

  function ensureDirectoryExistence(filePath) {
    var dirname = path.dirname(filePath);
    if (existsSyncFolder(dirname)) {
      return filePath;
    }
    ensureDirectoryExistence(dirname);
    fs.mkdirSync(dirname);
    return filePath;
  }

  var getTaskCheckExists = function(command, options) {
    options = options || {}
    return function() {
      var runFile = path.normalize(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/' + stampedFilePfx(new Date()) + 'where' + command + "out.txt");
      var p = nodeShellExec.apply(null, ['cmd', ['/c', 'where', command], { runFile : runFile } ])
      p.runFile = runFile;
      if (options.ignorefailures) {
        return p.then(function(v) { 
          // WScript.Echo('firstThen ' + v);
          return v })['catch']( function(e) { 
            console.error(e);
            // Ignore. Not a major error if where command fails !!!
            throw e;
        })
      }
      else return p.then(function() { 
        // WScript.Echo('firstThen ddd'); 
        return v });
    }
  }

  function verifyAndInstallPrerequisites() {
    fs.writeFileSync(ensureDirectoryExistence(downloadsdir + '/readme.txt'), getVersion() + ' Your local downloads for this instance');
    var downloadbatch =
    "::************************************************************************** \r\n \
    :Download_ <url> <File> \r\n \
      Powershell.exe ^\r\n \
      $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'; ^\r\n \
      [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols; ^\r\n \
      (New-Object System.Net.WebClient).DownloadFile('%1','%2') \r\n \
    exit /b \r\n \
    ::**************************************************************************";
    ensureDirectoryExistence(path.normalize(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/readme.txt'))
    fs.writeFileSync(path.normalize(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat'), downloadbatch);

    var downloadtasks = [];
    var installtasks = [];
    prerequisites.forEach(function(preq) {
      WScript.Echo(all)
      if(!all && preq.optional) return
      var p = preq.exists().then(function(exists) {
        if (exists && !preq.forceinstall) console.log( preq.shellcmd + ' exists');
        else {
          console.log('----------------------------')
          console.log(exists)  // PB : ??? Boolean true becomes -1 ???
          console.log('----------------------------')
          if(!exists) console.log(preq.shellcmd + ' is not installed');
          else console.log(preq.shellcmd + ' is installed but forceinstall was specifed so re-installing');
          return preq.preinstallsteps().then(function(){
            // console.log(' task.install : ' + preq.install)
            installtasks.push( function(){ return preq.install() } );
          })
        }
      })
      // .then(function(res){ console.log( 'preinstallsteps ' + res)});
      downloadtasks.push( p )
    });
    // console.log('downloadtasks')
    // console.dir(downloadtasks[0])
    var p = Promise.all(downloadtasks).then(function(){ 
      // console.log('calling install tasks : ' + installtasks.length)
      return any(installtasks) })

    // console.log('Promise.all.chain : ' + p.chain)
    return p 
  }

  // var choiceHandler = function(choices, choice) { 
  //   console.log('chosen : ' + choice)
  //   var decision = choices['d'];
  //   if (choice && choice === 'd' || !choice) {
  //     decision = choices['d']
  //   }
  //   else if (isNaN((+choice))) {
  //     decision = choice
  //   }
  //   else decision = choices[choice-1]
  //   if(!decision) throw 'Invalid selection : ' + decision
  //   return decision
  // }

  // prereq definition helpers. We can't do proper inheritance in cscript. So fallback to global functions.
  function exists(next){
    var self = this;
    console.log('checking existence of ' + self.shellcmd)
    return getTaskCheckExists(self.shellcmd, { ignorefailures: true })().then(function(exists) {
      // console.log('-------------exists=======================')
      // console.dir(exists)
      // console.log(self.shellcmd)
      // console.log(exists.messages.join(' ').indexOf(self.shellcmd))
      // console.log(exists + ' ' + self.shellcmd + '     exists')
      if(exists && (exists.messages.join(' ').indexOf(self.shellcmd) > -1) ) {
        return true;
      }
      else return false
    })['catch'](function(e){
      // console.log('-------------exists catch=======================')
      console.dir(e)
      return false;
    }) 
  }

  function install() { 
    var self = this; 
    var ifns = [self.installcmd]
    if(!ifns.map) ifns.map = map;
    console.log('Installing')
    return any(ifns.map(callsheltask))['catch'](function(e){ 
      if(e.code === 1602) {
        console.warn("Installation was probably cancelled.")
      }
      else throw e
    }).then(function(){
      return self.postinstallsteps && self.postinstallsteps()
    })
  }

  function unique(arr) {
      var hash = {}, result = [];
      for ( var i = 0, l = arr.length; i < l; ++i ) {
          if ( !hash.hasOwnProperty(arr[i]) ) { //it works with objects! in FF, at least
              hash[ arr[i] ] = true;
              result.push(arr[i]);
          }
      }
      return result;
  }

  // var ENV = Object.assign({}, process.env); // Shallow clone it.
  // WScript.echo( ENV("Path") )
  function sysAddPathVar(path){

    return true; // PB : TODO -- Not yet enabled. Remove sys path from path before saving.
    // Object.assign({
    //   inherit: true, shell: true, env: ENV, title: `${command} ${args}`
    // }, options)
    
    
    // var newpath = ENV("Path").split(';');
    // newpath = Array.from(new Set(newpath.push(path))).join(';')
    var newpath = [];
    newpath.push(path)
    newpath = unique(newpath).join(';')
    // path.split(';').forEach(pel => { var kv = pel.split('='); kv[0] === key ? null : newpath.push(pel);  } )
    return any([nodeShellExec('setx', [/*'/m',*/ 'PATH', '"%PATH%;' + newpath + '"' ])
        // , nodeShellExec('set', [/*'/m',*/ 'PATH', '"%PATH%;' + newpath + '"' ])
      ] );
  }

  var getCmdString = function(args){ return args[0] + ' '  + args[1].join(' ') }

  var getgitshelltask = function(args, onEachError)  { return function(){ 
      console.log('----------getgitshelltask--------------called')
      return nodeShellExec( "cmd", ['/c', getCmdString(args)], args[2]) 
        // .catch( onEachError || function(e){ console.error(e) }) 
    }
  }


  var createTasq = function(args, shellT, onEachError) {
    var tasq = shellT ? shellT(args) : ( function(){ 
      return any([nodeShellExec.apply(null, args)])
      // .catch( onEachError || function(e){ console.error(e) } )
    })
    tasq.toString = function(){ return JSON.stringify(args)}
    return tasq;
  }

  var execserial = function(tasklist, task, shellT, onEachError){
    var exec = function(taskArgs){ 
      console.log('executing ' + task)
      var thistask = task.concat();
      thistask[1] = thistask[1].concat()
      thistask[1].push.apply(thistask[1], taskArgs)
      return createTasq(thistask, shellT)
    }
  
    tasklist.map = map
    console.log('execlist ' + tasklist.map(exec))
    return any(tasklist.map(exec))
  }
  
  function resetgitconfig(){

    // https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage
    // Git Credential storage...
    // git config --global credential.helper 'store --file ~/.gitcredentials'
    // git credential-store --file ~/.gitcredentials store
    // // notepad C:/Program Files/Git/etc/gitconfig
    // git config --global --unset credential
    // git config --edit --system
    // git config --global credential.helper "store --file ~/gitcredentials"
    // git credential fill
    // git credential-store --file ~/git.store store 
    
    // Find system git config
    // git config --global --edit
    // git config --list --show-origin
    // git config --list --show-origin --show-scope
    
    
    // https://stackoverflow.com/questions/35942754/how-can-i-save-username-and-password-in-git
    // Recipie
    // git config --global --unset credentials.helper
    // cd /path/to/my/repo
    // git config --unset credential.helper
    // git config --global credential.helper "store --file ~/.elxrcredentials"
    // git config credential.helper 'store --file ~/.elxrcredentials'
      //-- git config credential.*.username my_user_name
      //-- git config credential.https://gitlab.com.username my_user_name
    // git credential fill

    // git config --global credential.modalprompt false // doesnst work.
    // core askpass = ;;; https://stackoverflow.com/questions/37182847/how-do-i-disable-git-credential-manager-for-windows
    var options = {
      inherit: true, shell: true //, env: process.env
      , cwd: selectedinstance.root
      // , runas: processedArgs.runas 
    }
    
    var task = ['git', ['config'], options] 
  
    var tasklist = [
        // ['--global', '--unset credentials.helper']
        // ['--unset', 'credentials.helper'] 
        // , 
        // ['credential.helper', `'store --file git_repo_credentials'`]
          ['--global', 'credential.helper', '"store --file ~/.elxrcredentials"']
        , ['--global', '--replace-all', 'user.name', gitUser]
        , ['--global', '--replace-all', 'user.email', gitEmail]
  
        // , ['--list']
        // , ['credential', 'fill']
    ]
    console.log(tasklist)
    var onEachError = function(e) { console.error( e.messages.join('\n') + e.result + '\n' + util.inspect(e)  + '\n') } 
    var shellT = function(args) { 
      return getgitshelltask(args, onEachError) 
      // .catch(e => console.error( e.messages.join('\n') + e.result + '\n' + util.inspect(e)  + '\n' + tasq.toString()) ) 
    }
    return execserial(tasklist, task, shellT, onEachError ).then(function(){
      // git credential-store --file ./.elxrcredentials store
      console.log('gathering git credentials')
      var userhome = '~'
      if(isWin()) userhome = '%userprofile%'
      var host = reposerver.replace('https://','').replace('http://','')
      var protocol = reposerver.replace('https://','') === host ? 'https' : 'http'
      fs.writeFileSync('ic.txt', 'host='+host+'\r\nprotocol='+protocol+'\r\nusername='+gitUser+'\r\npassword='+gitpassword)
      return nodeShellExec('git', ['credential-store', '--file', userhome + '/.elxrcredentials', 'store', '<', 'ic.txt'], options)
    }) 
  }

  var gitpostinstallsteps = function(){

    try {
      // Load just in case someone was smart enough to fill in before install.
      var icf = fs.readFileSync( 'installchoices.js')
      icf = icf.replace('module.exports = ', 'icf = ')
      console.log(icf)
      icf = eval(icf)
      // console.log(icf.reposerver)
      instanceName = icf.instanceName
      reposerver = icf.reposerver;
      gitUser = icf.gitUser;
      gitEmail = icf.gitEmail
    }
    catch(e) {
      console.error(e)
    }

    // gitpassword = icf.??? ; we do not store password it should already be cached in the credential store.

    // PB : TODO -- Detect failure or cancellation before attenpting postinstall steps...
    var steps = [];
    steps.push(
      function(){
        var choices = { 0 : 'elixir' }
        return cli.prompt(choices, 'instanceName', instanceName).then(function(choice){ instanceName = choice } )
      }
    )
    steps.push(
      function(){
        var choices = { 0 : 'https://git.bbh.org.in' }
        return cli.prompt(choices, 'reposerver', reposerver).then(function(choice){ reposerver = choice } )
      }
    )
    steps.push(
      function(){
        var choices = { 0 : 'guest', 1 : 'chessdemo' } 
        return cli.prompt(choices, 'git user name', gitUser).then(function(choice){ gitUser = choice } )
      }
    )
    steps.push(
      function(){
        var choices = { }
        return cli.prompt(choices, 'git password', gitpassword).then(function(choice){ gitpassword = choice } )
      }
    )
    steps.push(
      function(){
        var choices = { 0 : 'guest@bbh.org.in', 1 : 'chessdemo@bbh.org.in' }
        return cli.prompt(choices, 'git user email', gitEmail).then(function(choice){ gitEmail = choice })
      }
    )

    return any(steps).then(function(){
      // Save values to add credentials later in index.js...
      // console.log('module.exports = { instanceName : "'+ instanceName +'", reposerver: "'+reposerver+'", gitUser: "'+gitUser+'", gitEmail:"'+gitEmail+'" }')
      fs.writeFileSync( 'installchoices.js', 'module.exports = { instanceName : "'+ instanceName +'", reposerver: "'+reposerver+'", gitUser: "'+gitUser+'", gitEmail:"'+gitEmail+'" }' )
        
      var steps = [
        //   ['git', ['config', '--global', '--add', 'user.name', gitUser]]
        // , ['git', ['config', '--global', '--add', 'user.email', gitEmail]]
      ]
      
      if(!steps.map) steps.map = map;
      return any(steps.map(callsheltask)).then(function(){ 
        
      })
    });
  }

  var prerequisites = [
    {
      shellcmd: 'git',
      url: 'https://github.com/git-for-windows/git/releases/download/v2.33.0.windows.2/Git-2.33.0.2-64-bit.exe'
      , installer: 'Git-2.33.0.2-64-bit.exe'
      , installcmd: ['cmd', ['/c', 'start',
        '/WAIT', downloadsdir + '/' + 'Git-2.33.0.2-64-bit.exe'
        , '/VERYSILENT'
        // , '/MERGETASKS=!runcode' // This is required only for vscode...
      ]]
      , preinstallsteps: function() {
        var self = this;
        console.log('Git preinstall steps')

        var steps = [];
        // steps.push(
        //   function(){
        //     // console.log('cli prompt steps')
        //     var choices = { 0 : 'guest', 1 : 'chessdemo' }
        //     return cli.prompt(choices, 'git user name', gitUser).then(function(choice){ gitUser = choice } )
        //   }
        // )

        // steps.push(
        //   function(){
        //     var choices = { 0 : 'guest@bbh.org.in', 1 : 'chessdemo@bbh.org.in' }
        //     return cli.prompt(choices, 'git user email', gitEmail).then(function(choice){ gitEmail = choice })
        //   }
        // )
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', [self.url, downloadsdir + '/' + self.installer])
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
      , installsteps: function () {
        var self = this;
        console.log('Git Installsteps called')
        var ifns = [self.installcmd]
        if(!ifns.map) ifns.map = map;
        return any(ifns.map(callsheltask))['catch'](function(e){ 
          if(e.code === 1602) {
            console.warn("Installation was probably cancelled.")
          }
          else throw e
        })
      }
      , postinstallsteps: gitpostinstallsteps
      , install: function () {
        gitPreinstalled = false;
        var self = this;
        console.log('Git Install called')

        return any([ /*self.preinstallsteps,*/ 
            function(){ return self.installsteps() } 
          , function() { return resetgitconfig() }
          , function(){ return self.postinstallsteps() } 
        ])
      }
      // , verifyAndInstall : function(){
      //   var self = this;
      //   return self.exists().then( function(exits) {
      //     if(exists) return self.getUser(null, function(){ return self.postinstallsteps() } )
      //     else return self.install();
      //   })
      // }
      , exists : exists
      , getUser : function(repo, onNoResult){
        
        
        onNoResult = onNoResult || function(){return false}
        var globalOrLocal = '--global';
        if(!repo) globalOrLocal = '--global';
        else globalOrLocal = '--local'
        var fns = [['git', ['config', globalOrLocal, '--get-all', 'user.name']]]
        if(!ifns.map) ifns.map = map;
        return any(fns.map(callsheltask)).then(function(result){
          // not yet configured.
          if(!result.success) return onNoResult()
          else {
            var users = result.messages[0].trim().split('\n');
            if(users.length === 0 ||
                users.length === 1 && users[0] === 'guest') {
              
              return onNoResult()
            }
            else return users[0]; // PB : TODO == We should probably prompt with all the users available for selection ! 
          }
        })
        ['catch'](function(e){ 
          console.log(e) 
          return onNoResult()
        })
      }
    }
    ,
    {
      shellcmd: 'node',
      url: 'https://nodejs.org/dist/v14.17.6/node-v14.17.6-x64.msi'
      , installer: 'node-v14.17.3-x64.msi'
      , installcmd: ['MSIEXEC.exe', ['/i'
        , path.normalize(downloadsdir + '/' + 'node-v14.17.3-x64.msi')
        , 'ACCEPT=YES', '/passive']]
      , install : install
      , exists : exists
      , preinstallsteps: function() {
        var self = this;
        console.log('Node preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', ['"' + self.url + '"', downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
    }
    , { 
        shellcmd: 'python'
        , optional : true
        , url: 'https://www.python.org/ftp/python/2.7.18/python-2.7.18.amd64.msi'
        , installer: 'python-2.7.18.amd64.msi'
        , installcmd: ['MSIEXEC.exe', ['/i'
          , path.normalize(downloadsdir + '/' + 'python-2.7.18.amd64.msi')
          , 'ACCEPT=YES', '/passive']]
          , preinstallsteps: function() {
        var self = this;
        console.log(self.shellcmd + ' preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', [self.url, downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
	, install : install
        , postinstallsteps : function() { return sysAddPathVar('%USERPROFILE%\\AppData/Local\\Microsoft\\WindowsApps') }
        , exists : exists
    }
    , { 
      shellcmd: 'code'
      , optional : true
      , url: 'https://code.visualstudio.com/sha/download?build=stable&os=win32-x64-user'
      , installer: 'VSCodeUserSetup-x64-1.65.2.exe'
      , installcmd: ['cmd /c', ['start /WAIT ' + downloadsdir + '/' + 'VSCodeUserSetup-x64-1.65.2.exe /VERYSILENT'
          // , '/MERGETASKS=!runcode' // This is required only for vscode...
        ]]
      , preinstallsteps: function() {
        var self = this;
        console.log(self.shellcmd + ' preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              	console.log(self.url)
                return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', ['"' + self.url + '"', downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
      , install : install
      , postinstallsteps : function() { return sysAddPathVar('%USERPROFILE%\\AppData/Local\\Microsoft\\WindowsApps') }
      , exists : exists
    }
    ,  {
        //  C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Ssms.exe
        // SQL Management studio 2018
        shellcmd: 'Ssms.exe', 
        url: 'https://aka.ms/ssmsfullsetup' 
      , installer: 'SSMS-Setup-ENU.exe'
      , installcmd: ['cmd', ['/c', 'start',
          '/WAIT', downloadsdir + '/' + 'SSMS-Setup-ENU.exe'
          , '/IAcceptSQLServerLicenseTerms'
          //, 'SSMSInstallRoot=%systemdrive%\\Program Files (x86)\\Microsoft SQL Server\\140\\Tools\\Binn\\ManagementStudio'
        ]]
      , install : install
      , exists : function(){
        var self = this;
        var installedpath = 'C:\\Program Files (x86)\\Microsoft SQL Server Management Studio 18\\Common7\\IDE\\Ssms.exe'
        console.log('checking existence of ' + installedpath)
        return Promise.resolve(existsSync(installedpath)) 
      }
      , preinstallsteps: function() {
        var self = this;
        console.log('smss 2018 preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              console.log(self.url)
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', ['"' + self.url + '"', downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
    }
    ,  {
        // C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Ssms.exe
        shellcmd: 'Ssms.exe', // SQL Mgmt Studo 2017
        url: 'https://go.microsoft.com/fwlink/?linkid=2043154&clcid=0x409' 
      , installer: 'SSMS-Setup-ENU-2017.exe'
      , installcmd: ['cmd', ['/c', 'start',
          '/WAIT', downloadsdir + '/' + 'SSMS-Setup-ENU-2017.exe'
          , '/IAcceptSQLServerLicenseTerms'
          //, 'SSMSInstallRoot=%systemdrive%\\Program Files (x86)\\Microsoft SQL Server\\140\\Tools\\Binn\\ManagementStudio'
        ]]
      , install : install
      , exists : function(){
        return Promise.resolve(false)
        var self = this;
        // var installedpath = 'C:\\Program Files (x86)\\Microsoft SQL Server Management Studio 18\\Common7\\IDE\\Ssms.exe'
        var installedpath = 'C:\\Program Files (x86)\\Microsoft SQL Server\\140\\Tools\\Binn\\ManagementStudio\\Ssms.exe'
        console.log('checking existence of ' + installedpath)
        return Promise.resolve(existsSync(installedpath)) 
      }
      , preinstallsteps: function() {
        var self = this;
        console.log('smss preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              console.log(self.url)
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', ['"' + self.url + '"', downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
    }
    , {
      shellcmd: 'sqlservr.exe',
      url: 'https://go.microsoft.com/fwlink/?linkid=866658'
      , installer: 'SQL2019-SSEI-Expr.exe'
      , installcmd: ['cmd', ['/c', 'start',
          '/WAIT', downloadsdir + '/' + 'SQL2019-SSEI-Expr.exe'
          , ''
          //, 'SSMSInstallRoot=%systemdrive%\\Program Files (x86)\\Microsoft SQL Server\\140\\Tools\\Binn\\ManagementStudio'
        ]]
      , install : install
      , exists : function(){
        var self = this;
        var installedpath = 'C:\\Program Files\\Microsoft SQL Server\\MSSQL15.SQLEXPRESS\\MSSQL\\Binn\\sqlservr.exe'
        console.log('checking existence of ' + installedpath)
        return Promise.resolve(existsSync(installedpath)) 
      }
      , preinstallsteps: function() {
        var self = this;
        console.log('sqlexpress preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              console.log(self.url)
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', ['"' + self.url + '"', downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
    }
    ,  {
      shellcmd: 'mysql',
        url: 'https://dev.mysql.com/get/Downloads/MySQLInstaller/mysql-installer-community-8.0.30.0.msi' //'https://dev.mysql.com/get/Downloads/MySQLInstaller/mysql-installer-community-8.0.29.0.msi'
      , installer: 'mysql-installer-community-8.0.30.0.msi'
      , installcmd: ['MSIEXEC.exe', ['/i'
        , path.normalize(downloadsdir + '/' + 'mysql-installer-community-8.0.30.0.msi')
          //, 'ACCEPT=YES', '/passive'
	]]
      , install : install
      , exists : function(){
        var self = this;
        var mysqldpath = 'C:\\Program Files\\MySQL\\MySQL Server 8.0\\bin\\mysqld.exe'
        console.log('checking existence of ' + mysqldpath)
        return Promise.resolve(existsSync(mysqldpath)) 
      }
      , preinstallsteps: function() {
        var self = this;
        console.log('mysql preinstall steps')

        var steps = [];
        steps.push(
          function(){
            if (!existsSync(downloadsdir + '/' + self.installer)) {
              console.log(self.url)
              return nodeShellExec(selectedinstance.root + '/.elxr/run-' + runtimestamp + '/download.bat', [self.url, downloadsdir + '/' + self.installer]
                , { waitmsg : 'downloading '+ self.shellcmd +' please wait' })
            }
            else {
              console.log(self.installer + ' Already exits Download skipped.') 
              return Promise.resolve(true)
            }
          }
        )
        
        return any(steps)
        // return any([any(steps), any(prompts)])
      }
    }
    , 
    {
        shellcmd: 'elxr'
      , forceinstall : true
      , installcmd: [ isWin() ? 'npm.cmd' : 'npm' , ['link'], { cwd : selectedinstance.root + '\\elxr' /* cwd should be the cloned dir*/}]
      , preinstallsteps: function() {
        var self = this;
        console.log('Elxr preinstall steps')

        var steps = [];
        steps.push(
          
        )
        return any([any(steps)])
      }
      , installsteps: function () {
        var self = this;
        console.log('GIt config reconfirm called ' + gitPreinstalled)
        var installIfNotExists = function(){
          var steps = [];
          if(gitPreinstalled) {
            console.log('-------------------------------------')
            console.log('Git was already preinstalled - reconfirming installchoices')
            // Load from previous run and confirm.
            // var reposerver = 'https://git.bbh.org.in';
            // var gitUser = 'guest';
            // var gitEmail = 'guest@bbh.org.in';
            try {
              var icf = fs.readFileSync( 'installchoices.js')
              icf = icf.replace('module.exports = ', 'icf = ')
              console.log(icf)
              icf = eval(icf)
              // console.log(icf.reposerver)
              instanceName = icf.instanceName
              reposerver = icf.reposerver;
              gitUser = icf.gitUser;
              gitEmail = icf.gitEmail
            }
            catch(e) {
              console.error(e)
            }
             
            
            console.log('-------------------------------------')
            console.dir(icf)
            // var p = gitpostinstallsteps()
            steps.push(gitpostinstallsteps)
            // steps.push(
            //   function(){
            //     console.log('cli prompt steps')
            //     var choices = { 0 : 'http://git.bbh', 1 : 'https://git.bbh.org.in' }
            //     // console.dir(choices)
            //     return cli.prompt(choices, 'git repository : ', reposerver).then(function(choice){ reposerver = choice } )
            //   }
            // )
          }
          else{
            // var p = Promise.resolve(true)
            steps.push(
              function(){
                var choices = {  }
                return cli.prompt(choices, 'git password', gitpassword).then(function(choice){ gitpassword = choice } )
              }
            )
          }
          
        
          // resetgitconfig requires the password to be loaded from credential store. Currently just prompt for it.
          return any(steps).then( function(){
            console.log('calling resetgitconfig')
            return resetgitconfig().then(function(){
                console.log('cloning elxr now')
                var ifns = [               
                  ['git', ['clone', reposerver + '/chess/elxr'] ] 
                ]
              if(existsSyncFolder( selectedinstance.root + '\\elxr')) {
                if(existsSyncFolder( selectedinstance.root + '\\elxr\\.git')) {
                  // PB : TODO -- use a elxr guid signature to detect more reliably folders named the same that is not us.
                  ifns = [ ['git', ['pull'], { cwd : selectedinstance.root + '\\elxr' } ] ]
                }
                else {
                  throw 'elxr subfolder not recognized as a git repository. Please cleanup and continue.'
                }
              }
              else console.log(selectedinstance.root + '\\elxr' + ' NOT FOUND ')
              
              if(!ifns.map) ifns.map = map;
              return any(ifns.map(callsheltask)).then(function(){
                var pkgjson = selectedinstance.root + '\\elxr\\package.json' 
                var pkgcontents = fs.readFileSync(pkgjson)
                // PB : TODO -- Remove hardcode use proper replacement url..
                fs.writeFileSync( pkgjson, pkgcontents.replace(/http\:\/\/git\.bbh/g, reposerver));
                return callsheltask(['npm', ['i'], { cwd : '.\\elxr' } ])
              })['catch'](function(e){ 
                if(e.code === 1602) {
                  console.warn("Installation was probably cancelled.")
                }
                else throw e
                })
              
            })
          } )
        }
        var ifns = [ self.installcmd ]
        if(!ifns.map) ifns.map = map;
        return installIfNotExists().then(function(){
          return any(ifns.map(callsheltask))['catch'](function(e){ 
            if(e.code === 1602) {
              console.warn("Installation was probably cancelled.")
            }
            else throw e
          })
        })
      }
      , install: function () {
        var self = this;
        console.log('Elxr Install called')

        // PB : TODO -- detect external or internal and change elxr package.json dependencies to point to proper location.
        fs.writeFileSync(selectedinstance.root + '\\ei.bat', 'cmd /c node ' + selectedinstance.root + '\\elxr\\index.js i ' + instanceName)
        
        return any([ self.preinstallsteps, function(){ return self.installsteps() }
          , nodeShellExec('start', ['/WAIT', '""', '"' + selectedinstance.root + '\\ei.bat' + '"'])
          // , nodeShellExec('start', ['/WAIT', '""', '"C:\\Program Files\\Git\\bin\\sh.exe"', '-c', '"node ' + (selectedinstance.root + '\\elxr\\index.js').replace(/\\/g, '/') + ' i elixir"'])
          // , nodeShellExec('node', [selectedinstance.root + 'elxr\\index.js'])
        ])
        // return any([ /*self.preinstallsteps,*/ function(){ return self.installsteps() }
        //   , nodeShellExec('node', [selectedinstance.root + '\\elxr\\index.js', 'init'])
        //   // , nodeShellExec('node', [selectedinstance.root + 'elxr\\index.js'])
        // ])
      }
      // , exists : function(){
      //   console.log('Elxr PreInstallsteps called')
      //   var ifns = [ ['git', ['clone', 'http://git.bbh/chess\\elxr'] ] ]
      //   if(existsSyncFolder( selectedinstance.root + '\\elxr')) {
      //     if(existsSyncFolder( selectedinstance.root + '\\elxr\\.git')) {
      //       // PB : TODO -- use a elxr guid signature to detect more reliably folders named the same that is not us.
      //       ifns = [ ['git', ['pull'], { cwd : selectedinstance.root + '\\elxr' } ] ]
      //     }
      //     else {
      //       throw 'elxr subfolder not recognized as a git repository. Please cleanup and continue.'
      //     }
      //   }
      //   else console.log(selectedinstance.root + '\\elxr' + ' NOT FOUND ')
      //   if(!ifns.map) ifns.map = map;
      //   return any(ifns.map(callsheltask))['catch'](function(e){ 
      //     if(e.code === 1602) {
      //       console.warn("Installation was probably cancelled.")
      //     }
      //     else throw e
      //   })
      // }
      , exists : exists
      
    }
  ]

  if(!prerequisites.forEach) prerequisites.forEach = forEach;

  // nodeShellExec(selectedinstance.root + '/.elxr/run-' + '1629889572461' + '/download.bat'
  //   , ['https://github.com/git-for-windows/git/releases/download/v2.31.0.windows.1/Git-2.32.0.2-64-bit.exe'
  //   , downloadsdir + '/' + 'Git-2.32.0.2-64-bit.exe']).start()
  promises.push(verifyAndInstallPrerequisites())

  startPromises();
}