var crypto = require("crypto")
var lob = require('lob-enc');
var handshake_collect = require("./handshake").collect;
var log = require("./log")("Receive");
var route_cacher = require("./cache.js")
var route_cache = new route_cacher()
var dupe = route_cache.insert;

module.exports = {
  handshake : receive_handshake,
  channel   : receive_channel,
  type      : receive_type,
  decloak   : receive_decloak
}

function receive_type(packet, pipe){

  if(!packet || !pipe)
    return new Error('invalid mesh.receive args',typeof packet,typeof pipe);

  try {
    var head = JSON.parse(packet.head)
    return (head.json.type || "unknown json")
  } catch (e){
    return (packet.head.length === 0) ? "channel"
         : (packet.head.length === 1) ? "handshake"
         : "unknown binary";
  }
}

function receive_decloak(packet, pipe){
  if(!lob.isPacket(packet) && !(packet = lob.decloak(packet)))
    return new Error('invalid packet ' + typeof packet);

  if(packet.cloaked)
    pipe.cloaked = true;

  return packet;
}


function receive_channel(mesh, packet, pipe){
  var token = packet.body.slice(0,16).toString('hex');
  var link = mesh.index[token];
  if(!link)
  {
    var route = mesh.routes[token];
    if(route)
    {
      log.debug('routing packet to',route.path);
      var dupid = crypto.createHash('sha256').update(packet).digest('hex');
      if(dupe(dupid)) return log.debug('dropping duplicate');
      return route.send(packet, undefined, function(){});
    }
    log.debug('dropping unknown channel packet to',token);
    log.debug('mesh.index', Object.keys(mesh.index))
    return;
  }
  link.x.receive(packet)
      .then(function(inner){
        // this pipe is valid, if it hasn't been seen yet, we need to resync
        if(!link.seen[pipe.uid])
        {
          log.debug('never seen pipe',pipe.uid,pipe.path)
          link.addPipe(pipe,true); // must see it first
          process.nextTick(link.sync); // full resync in the background
        }

        // if channel exists, handle it
        var chan = link.x.channels[inner.json.c];
        if(chan && !inner.json.type)
        {
          if(chan.state == 'gone') return log.debug('incoming channel is gone');
          return chan.receive(inner);
        }

        // new channel open, valid?
        if(inner.json.err || typeof inner.json.type != 'string') return log.debug('invalid channel open',inner.json,link.hashname);

        // do we handle this type
        log.debug('new channel open',inner.json);

        // error utility for any open handler problems
        function bouncer(err)
        {
          if(!err) return;
          var json = {err:err};
          json.c = inner.json.c;
          log.debug('bouncing open',json);
          link.x.send({json:json});
        }

        // check all the extensions for any handlers of this type
        var args = {pipe:pipe};
        for(var i=0;i<mesh.extended.length;i++)
        {
          if(typeof mesh.extended[i].open != 'object') continue;
          var handler = mesh.extended[i].open[inner.json.type];
          if(typeof handler != 'function') continue;
          // set the link to be 'this' and be done
          handler.call(link, args, inner, bouncer);
          return;
        }

        // default bounce if not handled
        return bouncer('unknown type');
      })
      .catch(log.debug);

};

// cache incoming handshakes to aggregate them
var hcache = {};
setInterval(function hcachet(){hcache={}},60*1000);

function receive_handshake(mesh, packet, pipe){
  var link, atOld, atNew,inner;
  mesh.self.decrypt(packet)
           .then(function(inne){
             inner = inne;
             log.debug('inner',inner,inner.json,inner.body)

             // process the handshake info to find a link
             var token = crypto.createHash('sha256').update(packet.body.slice(0,16)).digest().slice(0,16);
             return handshake_collect(mesh, token.toString('hex'), inner, pipe, packet);
           }).then(function(lin){
             link = lin;
             if(!link || !link.x)
               throw new Error("No link/exchange"); // can't respond w/o an exchange

             log.debug("have link for incoming handshake", link._mesh.hashname.substr)
             atOld = link.x.at();
             return link.x.sync(packet, inner);
           })
           .then(function(sync){
             atNew = link.x.at();
             log.debug('handshake sync',sync,atOld,atNew);

             // always send handshake back if not in sync
             if(!sync)
               link.handshake()
                   .then(function(handshake){
                     link.x.sending(handshake, pipe)
                   })
                   .catch(log.debug);

             // new outgoing sync
             if(atNew > atOld)
             {
               log.debug('new outgoing sync');
               link.syncedAt = Date.now();
             }

             // when in sync or we sent a newer at, trust pipe
             if(atNew >= atOld)
               link.addPipe(pipe, true);

             // if the senders token changed, we need to reset
             if(link.x.session && link.sid != link.x.session.token.toString('hex'))
             {
               link.sid = link.x.session.token.toString('hex');
               log.debug('new session',link.sid);
               link.x.flush(); // any existing channels can resend
               link.setStatus(); // we're up
             }
           })
           .catch(function(er){
             log.debug("handshake error: ",er.stack)
           });


}

@@ -1522,16 +1522,17 @@
 acket to
+ 
 ',token)
@@ -1537,61 +1537,8 @@
 n);%0A
-    log.debug('mesh.index', Object.keys(mesh.index))%0A
     
@@ -1910,32 +1910,67 @@
 ground%0A        %7D
+%0A        log.debug(inner.json.type)
 %0A%0A        // if 
@@ -3668,30 +3668,13 @@
 nner
-,inner.json,inner.body
+.json
 )%0A%0A 
@@ -4190,16 +4190,23 @@
 e.substr
+(0, 8 )
 )%0A      
@@ -5013,24 +5013,133 @@
 ipe, true);%0A
+             //log.debug(%22checking session%22, link.x.session, link.sid, link.x.session.tocken.toString('hex'))
 %0A           
@@ -5203,18 +5203,74 @@
         
+//console.log(link.x.session, link.sid)%0A             //
 if
+ 
 (link.x.
@@ -5276,20 +5276,137 @@
 .session
- && 
+)%0A              //  console.log(%22link.x.session%22, link.x.session.token.toString(''))%0A             if(!link.x.session %7C%7C (
 link.sid
@@ -5438,32 +5438,33 @@
 toString('hex'))
+)
 %0A             %7B%0A

clean up the debug output for Receive