(() => {

  var s = require("Storage").readJSON("widancs.json",1)||{settings:{enabled:false, category:[1,2,4]}};
  var ENABLED = s.settings.enabled;
  var CATEGORY = s.settings.category;

  function advert(){
  NRF.setAdvertising([
      0x02, //length
      0x01, //flags
      0x06, //
      0x11, //length
      0x15, //solicited Service UUID
      0xD0,0x00,0x2D,0x12,0x1E,0x4B,
      0x0F,0xA4,
      0x99,0x4E,
      0xCE,0xB5,
      0x31,0xF4,0x05,0x79],{connectable:true,discoverable:true,interval:375});
  }
  
  var state = {
      gatt:null,
      ancs:null,
      current:{cat:0,uid:0},
      notqueue:[],
      msgTO:undefined,
      com:new Uint8Array([0,0,0,0,0,1,20,0,3,100,0]),
      buf:new Uint8Array(132),
      inp:0,
      store:function(b){
          var i = this.inp;
          if (i+b.length<=132){
              this.buf.set(b,i);
              this.inp+=b.length;
          }
      },
      gotmsg:function(){
          var n = this.inp;
          var vw = DataView(this.buf.buffer);
          if (n<8) return null;
          var tn=vw.getUint16(6,true);
          if (n<(tn+8)) return null;
          var mn=vw.getUint16(9+tn,true);
          if (n<(mn+tn+11)) return null;
          return {tlen:tn, mlen:mn}; 
      }
  };  

  //stop advertising when peripheral link disconnected
  if (!NRF.getGattforCentralServer && ENABLED && typeof SCREENACCESS!='undefined') 
  NRF.on('disconnect',function(reason){
      NRF.sleep();
  });

  if (ENABLED && typeof SCREENACCESS!='undefined') 
  NRF.on('connect',function(addr){
    if(NRF.getGattforCentralServer)
      do_bond(NRF.getGattforCentralServer(addr));
    else
      NRF.connect(addr).then(do_bond);
  });
        
  function do_bond(g) {
      var tval, ival;
      state.gatt = g;
      function cleanup(){
        drawIcon(0); //disconnect from iPhone
        delete state.gatt;
        delete state.ancs;
        if(!NRF.getGattforCentralServer) NRF.disconnect();
        setTimeout(()=>{NRF.wake();},500);
      }
      drawIcon(1); //connect from iPhone
      state.gatt.device.on('gattserverdisconnected', function(reason) {
         if (ival) clearInterval(ival);
         if (tval) clearInterval(tval); 
        cleanup();
      });
      E.on("kill",function(){
        state.gatt.disconnect().then(function(){NRF.sleep();});
      });      
      NRF.setSecurity({passkey:"123456",mitm:1,display:1});
      tval = setTimeout(function(){
          if (ival) clearInterval(ival);
          state.gatt.disconnect().then(cleanup);
      },10000);        
      state.gatt.startBonding().then(function(){
        ival = setInterval(function(){
            var sec = state.gatt.getSecurityStatus();
            if (!sec.connected) {clearInterval(ival); clearTimeout(tval); return;}
            if (sec.connected && sec.encrypted){
              clearInterval(ival);  
              clearTimeout(tval);
              drawIcon(2); //bonded to iPhone
              do_ancs(); 
              return;
            }
        },1000);
      }).catch(function(e){
        Terminal.println("ERROR "+e);
      });
  }
  
  function do_ancs() {
    state.ancs = {primary:null, notify:null, control:null, data:null};
    state.gatt.getPrimaryService("7905F431-B5CE-4E99-A40F-4B1E122D00D0").then(function(s) {
      state.ancs.primary=s;
      return s.getCharacteristic("9FBF120D-6301-42D9-8C58-25E699A21DBD");
    }).then(function(c) {
      state.ancs.notify=c;
      return state.ancs.primary.getCharacteristic("69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9");      
    }).then(function(c) {
      state.ancs.control=c;
      return state.ancs.primary.getCharacteristic("22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB");
    }).then(function(c) {
      state.ancs.data =c;
      drawIcon(3);//got remote services
      state.ancs.notify.on('characteristicvaluechanged', function(ev) {
        getnotify(ev.target.value);
      });
      state.ancs.data.on('characteristicvaluechanged', function(e) {
        state.store(e.target.value.buffer);
        var inds = state.gotmsg();
        if (inds) printmsg(state.buf,inds);        
      });
      state.ancs.notify.startNotifications().then(function(){
           state.ancs.data.startNotifications().then(function(){
              drawIcon(4); //ready for messages
           });  
      });
    }).catch(function(e){
        Terminal.println("ERROR "+e);
    });
  }
  
  function wordwrap(s){
    var txt = s.split("\n");
    var MAXCHARS = 18;
    for (var i = 0; i < txt.length; i++) {
      txt[i] = txt[i].trim();
      var l = txt[i];
      if (l.length > MAXCHARS) {
        var p = MAXCHARS;
        while (p > MAXCHARS - 8 && !" \t-_".includes(l[p]))
          p--;
        if (p == MAXCHARS - 8) p = MAXCHARS;
        txt[i] = l.substr(0, p);
        txt.splice(i + 1, 0, l.substr(p));
      }
    }
    return txt.join("\n");
  }
  
  
  var buzzing =false;  
  var screentimeout = undefined;
  var inalert = false;
  
  function release_screen(){
    screentimeout= setTimeout(() => { 
        SCREENACCESS.release(); 
        screentimeout = undefined; 
        inalert=false; 
        next_notify();
    }, 500);
  } 

  function printmsg(buf,inds){

    function send_action(tf){
      var bb = new Uint8Array(6);
      var v = DataView(bb.buffer);
      v.setUint8(0,2);
      v.setUint32(1,state.current.uid,true);
      v.setUint8(5,tf?0:1 );
      state.ancs.control.writeValue(bb).then(release_screen);        
    }

    if (state.msgTO) clearTimeout(state.msgTO); 
    var title="";
    for (var i=8;i<8+inds.tlen; ++i) title+=String.fromCharCode(buf[i]);
    var message = "";
    for (var j=11+inds.tlen;j<11+inds.tlen+inds.mlen;++j) { 
      message+=String.fromCharCode(buf[j]);
    } 
    message = wordwrap(message);
    //we may already be displaying a prompt, so clear it
    E.showPrompt();
    if (screentimeout) clearTimeout(screentimeout);
    if (!(require('Storage').readJSON('setting.json',1)||{}).quiet) {
      Bangle.setLCDPower(true);
    }
    SCREENACCESS.request();
    if (!buzzing && !(require('Storage').readJSON('setting.json',1)||{}).quiet){
        buzzing=true;
        Bangle.buzz(500).then(()=>{buzzing=false;});
    }
    if (state.current.cat!=1){
      E.showAlert(message,title).then(send_action.bind(null,false));
    } else {
      E.showPrompt(message,{title:title,buttons:{"Accept":true,"Cancel":false}}).then(send_action);
    }
  }

  var notifyTO;
  function getnotify(d){
    var eid = d.getUint8(0);
    var ct = d.getUint8(2);
    var id = d.getUint32(4,true);
    if (eid>1) return;
    if (notifyTO) clearTimeout(notifyTO);
    if(!CATEGORY.includes(ct)) return; 
    var len = state.notqueue.length;
    if (ct == 1) { // it's a call so pre-empt
        if (inalert) {state.notqueue.push(state.current); inalert=false;}
        state.notqueue.push({cat:ct, uid:id});
    } else if (len<16)
        state.notqueue[len] = {cat:ct, uid:id};
    notifyTO = setTimeout(next_notify,1000);
  }

  function next_notify(){
      if(state.notqueue.length==0 || inalert) return;
      inalert=true;
      state.current = state.notqueue.pop();
      var v = DataView(state.com.buffer);
      if (state.current.cat==6) v.setUint8(8,2); else v.setUint8(8,3);//get email title
      v.setUint32(1,state.current.uid,true);
       state.inp=0;
       state.ancs.control.writeValue(state.com).then(function(){
            state.msgTO=setTimeout(()=>{
               inalert=false;
               state.msgTO=undefined;
               next_notify();
               },1000);
       });
  }

  var stage = 5;    
  //grey, pink, lightblue, yellow, green
  function draw(){
    var colors = new Uint16Array([0xc618,0xf818,0x3ff,0xffe0,0x07e0,0x0000]);
    var img = E.toArrayBuffer(atob("GBgBAAAABAAADgAAHwAAPwAAf4AAP4AAP4AAP4AAHwAAH4AAD8AAB+AAA/AAAfgAAf3gAH/4AD/8AB/+AA/8AAf4AAHwAAAgAAAA"));
    g.setColor(colors[stage]);
    g.drawImage(img,this.x,this.y);
  }
    
  WIDGETS["ancs"] ={area:"tl", width:24,draw:draw};
    
  function drawIcon(id){
    stage = id;
    WIDGETS["ancs"].draw();
  }
  
  if (ENABLED && typeof SCREENACCESS!='undefined') {
    stage = 0;
    NRF.setServices(undefined,{uart:false});
    NRF.sleep();
    NRF.wake();
    advert();
  }
  
  })();
  
  
  