package network

import (
	crand "crypto/rand"
	"encoding/binary"
	"time"

	//"fmt"

	"github.com/Arceliar/phony"

	"github.com/Arceliar/ironwood/types"
)

/***********
 * router *
 ***********/

/*

Potential showstopping issue (long term):
  Greedy routing using coords is fundamentally insecure.
    Nothing prevents a node from advertising the same port number to two different children.
    Everything downstream of the attacker is at risk of random blackholes etc.
    This costs the attacker essentially nothing.
  Workaround: use full keys.
    That obviously won't work for normal traffic -- it's too much info.
    It *may* work for protocol traffic, so we can use it for pathfinding.
    We could then e.g. build a source route along the way, and use the source route... if we can do that securely...
    Added benefit, we do expect source routing to be more stable in the face of tree flapping...
  Obvious issues with ygg v0.4 style source routing... alternatives?
    Detect if we've visited the same node before so we can drop traffic? How?
      Bloom filter would work, except for the issue of false positives...
      If we store a reverse route, we could send back an error, so the sender can resize the bloom filter... Seems messy...
    Bloom filter to track visited nodes, and if in the filter then add to a list? If in the list already, drop traffic entirely?

*/

const routerUnknownLatency = time.Duration(^uint32(0))

type router struct {
	phony.Inbox
	core       *core
	pathfinder pathfinder                           // see pathfinder.go
	blooms     blooms                               // see bloomfilter.go
	peers      map[publicKey]map[*peer]struct{}     // True if we're allowed to send a mirror to this peer (but have not done so already)
	sent       map[publicKey]map[publicKey]struct{} // tracks which info we've sent to our peer
	ports      map[peerPort]publicKey               // used in tree lookups
	infos      map[publicKey]routerInfo
	timers     map[publicKey]*time.Timer
	ancs       map[publicKey][]publicKey // Peer ancestry info
	cache      map[publicKey][]peerPort  // Cache path slice for each peer
	lags       map[*peer]time.Duration   // Latency for a given *peer to respond with a valid sigres, exponentially weighted average
	requests   map[publicKey]routerSigReq
	responses  map[publicKey]routerSigRes
	responded  map[*peer]struct{}
	resSeqs    map[publicKey]uint64
	resSeqCtr  uint64
	refresh    bool
	doRoot1    bool
	doRoot2    bool
	mainTimer  *time.Timer
}

func (r *router) init(c *core) {
	r.core = c
	r.pathfinder.init(r)
	r.blooms.init(r)
	r.peers = make(map[publicKey]map[*peer]struct{})
	r.sent = make(map[publicKey]map[publicKey]struct{})
	r.ports = make(map[peerPort]publicKey)
	r.infos = make(map[publicKey]routerInfo)
	r.timers = make(map[publicKey]*time.Timer)
	r.ancs = make(map[publicKey][]publicKey)
	r.cache = make(map[publicKey][]peerPort)
	r.lags = make(map[*peer]time.Duration)
	r.requests = make(map[publicKey]routerSigReq)
	r.responses = make(map[publicKey]routerSigRes)
	r.responded = make(map[*peer]struct{})
	r.resSeqs = make(map[publicKey]uint64)
	// Kick off actor to do initial work / become root
	r.mainTimer = time.AfterFunc(time.Second, func() {
		r.Act(nil, r._doMaintenance)
	})
	r.doRoot2 = true
	r.Act(nil, r._doMaintenance)
}

func (r *router) _doMaintenance() {
	if r.mainTimer == nil {
		return
	}
	r.doRoot2 = r.doRoot2 || r.doRoot1
	r._resetCache() // Resets path caches, since that info may no longer be good, TODO? don't wait for maintenance to do this
	r._updateAncestries()
	r._fix()           // Selects new parent, if needed
	r._sendAnnounces() // Sends announcements to peers, if needed
	r.blooms._doMaintenance()
	r.mainTimer.Reset(time.Second)
}

func (r *router) _shutdown() {
	if r.mainTimer != nil {
		r.mainTimer.Stop()
		r.mainTimer = nil
	}
	// TODO clean up pathfinder etc...
	//  There's a lot more to do here
}

func (r *router) _resetCache() {
	for k := range r.cache {
		delete(r.cache, k)
	}
}

func (r *router) addPeer(from phony.Actor, p *peer) {
	r.Act(from, func() {
		//r._resetCache()
		if _, isIn := r.peers[p.key]; !isIn {
			r.peers[p.key] = make(map[*peer]struct{})
			r.sent[p.key] = make(map[publicKey]struct{})
			r.ports[p.port] = p.key
			r.blooms._addInfo(p.key)
		} else {
			// Send anything we've already sent over previous peer connections to this node
			for k := range r.sent[p.key] {
				if info, isIn := r.infos[k]; isIn {
					p.sendAnnounce(r, info.getAnnounce(k))
				} else {
					panic("this should never happen")
				}
			}
		}
		r.peers[p.key][p] = struct{}{}
		r.lags[p] = routerUnknownLatency // High enough but not high enough to overflow uint64
		if _, isIn := r.requests[p.key]; !isIn {
			r.requests[p.key] = *r._newReq()
		}
		req := r.requests[p.key]
		delete(r.responded, p)
		p.sendSigReq(r, &req)
		r.blooms._sendBloom(p)
	})
}

func (r *router) removePeer(from phony.Actor, p *peer) {
	r.Act(from, func() {
		//r._resetCache()
		ps := r.peers[p.key]
		delete(ps, p)
		delete(r.lags, p)
		delete(r.responded, p)
		if len(ps) == 0 {
			delete(r.peers, p.key)
			delete(r.sent, p.key)
			delete(r.ports, p.port)
			delete(r.requests, p.key)
			delete(r.responses, p.key)
			delete(r.resSeqs, p.key)
			delete(r.ancs, p.key)
			delete(r.cache, p.key)
			r.blooms._removeInfo(p.key)
			//r._fix()
		} else {
			// The bloom the remote node is tracking could be wrong due to a race
			// TODO? don't send it immediately, reset the "sent" state to blank so we'll resend next maintenance period
			for p := range ps {
				r.blooms._sendBloom(p)
			}
		}
	})
}

func (r *router) _clearReqs() {
	for k := range r.requests {
		delete(r.requests, k)
	}
	for k := range r.responses {
		delete(r.responses, k)
	}
	for k := range r.resSeqs {
		delete(r.resSeqs, k)
	}
	r.resSeqCtr = 0
}

func (r *router) _sendReqs() {
	r._clearReqs()
	for pk, ps := range r.peers {
		req := r._newReq()
		r.requests[pk] = *req
		for p := range ps {
			delete(r.responded, p)
			p.sendSigReq(r, req)
		}
	}
}

func (r *router) _updateAncestries() {
	for pkey := range r.peers {
		anc := r._getAncestry(pkey)
		old := r.ancs[pkey]
		var diff bool
		if len(anc) != len(old) {
			diff = true
		} else {
			for idx := range anc {
				if anc[idx] != old[idx] {
					diff = true
					break
				}
			}
		}
		if diff {
			r.ancs[pkey] = anc
		}
	}
}

func (r *router) _getCost(p *peer) uint64 {
	// Note that cost needs to be non-zero, used in multiplication and division
	c := uint64(r.lags[p].Milliseconds())
	if c == 0 {
		c = 1
	}
	return c
}

func (r *router) _fix() {
	bestRoot := r.core.crypto.publicKey
	bestParent := r.core.crypto.publicKey
	bestCost := ^uint64(0)
	self := r.infos[r.core.crypto.publicKey]
	// Check if our current parent leads to a better root than ourself
	if _, isIn := r.peers[self.parent]; isIn {
		root, dists := r._getRootAndDists(r.core.crypto.publicKey)
		if root.less(bestRoot) {
			cost := ^uint64(0)
			for p := range r.peers[self.parent] {
				// Use the path to the root as our benchmark for parent selection
				c := dists[root] * r._getCost(p)
				if c < cost {
					cost = c
				}
			}
			bestRoot, bestParent, bestCost = root, self.parent, cost
		}
	}
	// Check if we know a better root/parent
	for pk := range r.responses {
		if _, isIn := r.infos[pk]; !isIn {
			// We don't know where this peer is
			continue
		}
		pRoot, pDists := r._getRootAndDists(pk)
		if _, isIn := pDists[r.core.crypto.publicKey]; isIn {
			// This would loop through us already
			continue
		}
		cost := ^uint64(0)
		for p := range r.peers[pk] {
			// Use the path to the root as our benchmark for parent selection
			c := pDists[pRoot] * r._getCost(p)
			if c < cost {
				cost = c
			}
		}
		if pRoot.less(bestRoot) {
			bestRoot, bestParent, bestCost = pRoot, pk, cost
		} else if pRoot != bestRoot {
			continue // wrong root
		}
		if (r.refresh && cost*2 < bestCost) || (bestParent != self.parent && cost < bestCost) {
			// It's time to refresh our self info
			// If we're going to change to a better parent, now seems like the time...
			bestRoot, bestParent, bestCost = pRoot, pk, cost
		}
	}
	if r.refresh || r.doRoot1 || r.doRoot2 || self.parent != bestParent {
		res, isIn := r.responses[bestParent]
		switch {
		case isIn && bestRoot != r.core.crypto.publicKey && r._useResponse(bestParent, &res):
			// Somebody else should be root
			// Note that it's possible our current parent hasn't sent a res for our current req
			// (Link failure in progress, or from bad luck with timing)
			r.refresh = false
			r.doRoot1 = false
			r.doRoot2 = false
			r._sendReqs()
		case r.doRoot2:
			// Become root
			if !r._becomeRoot() {
				panic("this should never happen")
			}
			/*
				self = r.infos[r.core.crypto.publicKey]
				ann := self.getAnnounce(r.core.crypto.publicKey)
				for _, ps := range r.peers {
					for p := range ps {
						p.sendAnnounce(r, ann)
					}
				}
			*/
			r.refresh = false
			r.doRoot1 = false
			r.doRoot2 = false
			r._sendReqs()
		case !r.doRoot1:
			r.doRoot1 = true
			// No need to sendReqs in this case
			//  either we already have a req, or we've already requested one
			//  so resetting and re-requesting is just a waste of bandwidth
		default:
			// We need to self-root, but we already started a timer to do that later
			// So this is a no-op
		}
	}
}

func (r *router) _sendAnnounces() {
	// This is insanely delicate, lots of correctness is implicit across how nodes behave
	// Change nothing here.
	selfAnc := r._getAncestry(r.core.crypto.publicKey)
	var toSend []publicKey
	var anns []*routerAnnounce

	for peerKey, sent := range r.sent {
		// Initial setup stuff
		toSend = toSend[:0]
		anns = anns[:0]
		peerAnc := r._getAncestry(peerKey)

		// Get whatever we haven't sent from selfAnc
		for _, k := range selfAnc {
			if _, isIn := sent[k]; !isIn {
				toSend = append(toSend, k)
				sent[k] = struct{}{}
			}
		}

		// Get whatever we haven't sent from peerAnc
		for _, k := range peerAnc {
			if _, isIn := sent[k]; !isIn {
				toSend = append(toSend, k)
				sent[k] = struct{}{}
			}
		}

		/*
			// Reset sent so it only contains the ancestry info
			for k := range sent {
				delete(sent, k)
			}
			for _, k := range selfAnc {
				sent[k] = struct{}{}
			}
			for _, k := range peerAnc {
				sent[k] = struct{}{}
			}
		*/

		// Now prepare announcements
		for _, k := range toSend {
			if info, isIn := r.infos[k]; isIn {
				anns = append(anns, info.getAnnounce(k))
			} else {
				panic("this should never happen")
			}
		}

		// Send announcements
		for p := range r.peers[peerKey] {
			for _, ann := range anns {
				p.sendAnnounce(r, ann)
			}
		}
	}
}

func (r *router) _newReq() *routerSigReq {
	var req routerSigReq
	nonce := make([]byte, 8)
	crand.Read(nonce) // If there's an error, there's not much to do...
	req.nonce = binary.BigEndian.Uint64(nonce)
	req.seq = r.infos[r.core.crypto.publicKey].seq + 1
	return &req
}

func (r *router) _becomeRoot() bool {
	req := r._newReq()
	res := routerSigRes{
		routerSigReq: *req,
		port:         0, // TODO? something else?
	}
	res.psig = r.core.crypto.privateKey.sign(res.bytesForSig(r.core.crypto.publicKey, r.core.crypto.publicKey))
	ann := routerAnnounce{
		key:          r.core.crypto.publicKey,
		parent:       r.core.crypto.publicKey,
		routerSigRes: res,
		sig:          res.psig,
	}
	if !ann.check() {
		panic("this should never happen")
	}
	return r._update(&ann)
}

func (r *router) _handleRequest(p *peer, req *routerSigReq) {
	res := routerSigRes{
		routerSigReq: *req,
		port:         p.port,
	}
	res.psig = r.core.crypto.privateKey.sign(res.bytesForSig(p.key, r.core.crypto.publicKey))
	p.sendSigRes(r, &res)
}

func (r *router) handleRequest(from phony.Actor, p *peer, req *routerSigReq) {
	r.Act(from, func() {
		r._handleRequest(p, req)
	})
}

func (r *router) _handleResponse(p *peer, res *routerSigRes, rtt time.Duration) {
	if _, isIn := r.responses[p.key]; !isIn && r.requests[p.key] == res.routerSigReq {
		r.resSeqCtr++
		r.resSeqs[p.key] = r.resSeqCtr
		r.responses[p.key] = *res
		//r._fix() // This could become our new parent
	}
	if _, isIn := r.responded[p]; !isIn && r.requests[p.key] == res.routerSigReq {
		r.responded[p] = struct{}{}
		lag := r.lags[p]
		if lag == routerUnknownLatency {
			r.lags[p] = rtt * 2 // Start new links with a penalty to prioritize stability
		} else {
			prev := lag
			lag = lag * 7 / 8
			lag += min(rtt, prev*2) / 8
			r.lags[p] = lag
		}
	}
}

func (r *router) _useResponse(peerKey publicKey, res *routerSigRes) bool {
	bs := res.bytesForSig(r.core.crypto.publicKey, peerKey)
	info := routerInfo{
		parent:       peerKey,
		routerSigRes: *res,
		sig:          r.core.crypto.privateKey.sign(bs),
	}
	ann := info.getAnnounce(r.core.crypto.publicKey)
	if r._update(ann) {
		/*
			for _, ps := range r.peers {
				for p := range ps {
					p.sendAnnounce(r, ann)
				}
			}
		*/
		return true
	}
	return false
}

func (r *router) handleResponse(from phony.Actor, p *peer, res *routerSigRes, rtt time.Duration) {
	r.Act(from, func() {
		r._handleResponse(p, res, rtt)
	})
}

func (r *router) _update(ann *routerAnnounce) bool {
	if info, isIn := r.infos[ann.key]; isIn {
		switch {
		// Note: This logic *must* be the same on every node
		// If that's not true, then peers can infinitely spam announcements at each other for expired infos
		/*********************************
		 * XXX *** DO NOT CHANGE *** XXX *
		 *********************************/
		case info.seq > ann.seq:
			// This is an old seq, so exit
			return false
		case info.seq < ann.seq:
			// This is a newer seq, so don't exit
		case info.parent.less(ann.parent):
			// same seq, worse (higher) parent
			return false
		case ann.parent.less(info.parent):
			// same seq, better (lower) parent, so don't exit
		case ann.nonce < info.nonce:
			// same seq and parent, lower nonce, so don't exit
		default:
			// same seq and parent, same or worse nonce, so exit
			return false
		}
	}
	// Clean up sent info and cache
	for _, sent := range r.sent {
		delete(sent, ann.key)
	}
	r._resetCache()
	// Save info
	info := routerInfo{
		parent:       ann.parent,
		routerSigRes: ann.routerSigRes,
		sig:          ann.sig,
	}
	key := ann.key
	var timer *time.Timer
	if key == r.core.crypto.publicKey {
		delay := r.core.config.routerRefresh // TODO? slightly randomize
		timer = time.AfterFunc(delay, func() {
			r.Act(nil, func() {
				if r.timers[key] == timer {
					r.refresh = true
					//r._fix()
				}
			})
		})
	} else {
		timer = time.AfterFunc(r.core.config.routerTimeout, func() {
			r.Act(nil, func() {
				if r.timers[key] == timer {
					timer.Stop() // Shouldn't matter, but just to be safe...
					delete(r.infos, key)
					delete(r.timers, key)
					for _, sent := range r.sent {
						delete(sent, key)
					}
					r._resetCache()
					//r._fix()
				}
			})
		})
	}
	if oldTimer, isIn := r.timers[key]; isIn {
		oldTimer.Stop()
	}
	r.timers[ann.key] = timer
	r.infos[ann.key] = info
	return true
}

func (r *router) _handleAnnounce(p *peer, ann *routerAnnounce) {
	if r._update(ann) {
		if ann.key == r.core.crypto.publicKey {
			// We just updated our own info from a message we received by a peer
			// That suggests we went offline, so our seq reset when we came back
			// The info they sent us could have been expired (see below in this function)
			// So we need to set that an update is required, as if our refresh timer has passed
			r.refresh = true
		}
		// No point in sending this back to the original sender
		r.sent[p.key][ann.key] = struct{}{}
		//r._fix() // This could require us to change parents
	} else {
		// We didn't accept the info, because we alerady know it or something better
		info := routerInfo{
			parent:       ann.parent,
			routerSigRes: ann.routerSigRes,
			sig:          ann.sig,
		}
		if oldInfo := r.infos[ann.key]; info != oldInfo {
			// They sent something, but it was worse
			// Should we tell them what we know
			// Only to the p that sent it, since we'll spam the rest as messages arrive...
			r.sent[p.key][ann.key] = struct{}{}
			p.sendAnnounce(r, oldInfo.getAnnounce(ann.key))
		} else {
			// They sent us exactly the same info we already have
			// No point in sending it back when we do maintenance
			r.sent[p.key][ann.key] = struct{}{}
		}
	}
}

func (r *router) handleAnnounce(from phony.Actor, p *peer, ann *routerAnnounce) {
	r.Act(from, func() {
		r._handleAnnounce(p, ann)
	})
}

func (r *router) sendTraffic(tr *traffic) {
	// This must be non-blocking, to prevent deadlocks between read/write paths in the encrypted package
	// Basically, WriteTo and ReadFrom can't be allowed to block each other, but they could if we allowed backpressure here
	// There may be a better way to handle this, but it practice it probably won't be an issue (we'll throw the packet in a queue somewhere, or drop it)
	r.Act(nil, func() {
		r.pathfinder._handleTraffic(tr)
	})
}

func (r *router) handleTraffic(from phony.Actor, tr *traffic) {
	r.Act(from, func() {
		if p := r._lookup(tr.path, &tr.watermark); p != nil {
			p.sendTraffic(r, tr)
		} else if tr.dest == r.core.crypto.publicKey {
			r.pathfinder._resetTimeout(tr.source)
			r.core.pconn.handleTraffic(r, tr)
		} else {
			// Not addressed to us, and we don't know a next hop.
			// The path is broken, so do something about that.
			r.pathfinder._doBroken(tr)
		}
	})
}

func (r *router) _getRootAndDists(dest publicKey) (publicKey, map[publicKey]uint64) {
	// This returns the distances from the destination's root for the destination and each of its ancestors
	// Note that we skip any expired infos
	dists := make(map[publicKey]uint64)
	next := dest
	var root publicKey
	var dist uint64
	for {
		if _, isIn := dists[next]; isIn {
			break
		}
		if info, isIn := r.infos[next]; isIn {
			root = next
			dists[next] = dist
			dist++
			next = info.parent
		} else {
			break
		}
	}
	return root, dists
}

func (r *router) _getRootAndPath(dest publicKey) (publicKey, []peerPort) {
	var ports []peerPort
	visited := make(map[publicKey]struct{})
	var root publicKey
	next := dest
	for {
		if _, isIn := visited[next]; isIn {
			// We hit a loop
			return dest, nil
		}
		if info, isIn := r.infos[next]; isIn {
			root = next
			visited[next] = struct{}{}
			if next == info.parent {
				// We reached a root, don't append the self port (it should be zero anyway)
				break
			}
			ports = append(ports, info.port)
			next = info.parent
		} else {
			// We hit a dead end
			return dest, nil
		}
	}
	// Reverse order, since we built this from the node to the root
	for left, right := 0, len(ports)-1; left < right; left, right = left+1, right-1 {
		ports[left], ports[right] = ports[right], ports[left]
	}
	return root, ports
}

func (r *router) _getDist(destPath []peerPort, key publicKey) uint64 {
	// We cache the keyPath to avoid allocating slices for every lookup
	var keyPath []peerPort
	if cached, isIn := r.cache[key]; isIn {
		keyPath = cached
	} else {
		_, keyPath = r._getRootAndPath(key)
		r.cache[key] = keyPath
	}
	end := len(destPath)
	if len(keyPath) < end {
		end = len(keyPath)
	}
	dist := uint64(len(keyPath) + len(destPath))
	for idx := 0; idx < end; idx++ {
		if keyPath[idx] == destPath[idx] {
			dist -= 2
		} else {
			break
		}
	}
	return dist
}

func (r *router) _lookup(path []peerPort, watermark *uint64) *peer {
	// Look up the next hop (in treespace) towards the destination
	var bestPeer *peer
	bestDist := ^uint64(0)
	if watermark != nil {
		if dist := r._getDist(path, r.core.crypto.publicKey); dist < *watermark {
			bestDist = dist // Self dist, so other nodes must be strictly better by distance
			*watermark = dist
		} else {
			return nil
		}
	}
	// Firstly work out which of the peers take us closer to the destination,
	// ignoring the link costs altogether, to guarantee loop-free next-hops.
	// TODO: Over 1024 peers, this may reallocate and escape to the heap.
	var _candidates [1024]*peer
	candidates := _candidates[:0]
	for k, ps := range r.peers {
		if dist := r._getDist(path, k); dist < bestDist {
			for p := range ps {
				candidates = append(candidates, p)
			}
		}
	}
	// Then take a look at the candidates and work out which is the best
	// tree distance, accounting for the link cost.
	bestPeer = nil
	//selfDist := bestDist
	bestCost := ^uint64(0)
	bestDist = ^uint64(0)
	for _, p := range candidates {
		// We want to minimize cost of the link and the remaining distance to the destination
		// If cost == latency, this is the same as maximizing velocity in treespace towards the destination
		dist := r._getDist(path, p.key)
		cost := r._getCost(p)
		accept := func() {
			bestPeer, bestCost, bestDist = p, cost, dist
		}
		switch {
		case bestPeer == nil:
			// Start with the first candidate to try & improve upon.
			accept()
		case p.key == bestPeer.key && p.prio < bestPeer.prio:
			// If the key is the same, select the link with the lowest priority.
			accept()
		case p.key == bestPeer.key && p.prio > bestPeer.prio:
			// If the key is the same, ignore links with higher priorities.
			continue
		case cost*dist < bestCost*bestDist:
			// This is the main goal, minimize cost of link * remaining distance to destination.
			accept()
		case cost*dist > bestCost*bestDist:
			// The cost*dist is worse, so skip.
			continue
		case dist < bestDist:
			// The cost*dist is the same but the tree distance is better.
			accept()
		case dist > bestDist:
			// Tree distance is worse, so skip.
			continue
		case cost < bestCost:
			// The cost*dist and tree dist are the same, which probably means tree dist is 0.
			// In that case, just go with the better cost.
			accept()
		case cost > bestCost:
			continue
		case p.order < bestPeer.order:
			// Both cost and distance are the same,
			// so use the peer that has been up the longest.
			accept()
		}
	}
	return bestPeer
}

func (r *router) _getAncestry(key publicKey) []publicKey {
	// Returns the ancestry starting with the root side, ordering is important for how we send over the network / GC info...
	var _anc [32]publicKey
	anc := r._backwardsAncestry(_anc[:0], key)
	for left, right := 0, len(anc)-1; left < right; left, right = left+1, right-1 {
		anc[left], anc[right] = anc[right], anc[left]
	}
	return anc
}

func (r *router) _backwardsAncestry(anc []publicKey, key publicKey) []publicKey {
	// Return an ordered list of node ancestry, starting with the given key and ending at the root (or the end of the line)
	here := key
	for {
		// TODO? use a map or something to check visited nodes faster?
		for _, k := range anc {
			if k == here {
				return anc
			}
		}
		if info, isIn := r.infos[here]; isIn {
			anc = append(anc, here)
			here = info.parent
			continue
		}
		// Dead end
		return anc
	}
}

/*****************
 * routerSigReq *
 *****************/

type routerSigReq struct {
	seq   uint64
	nonce uint64
}

func (req *routerSigReq) bytesForSig(node, parent publicKey) []byte {
	out := make([]byte, 0, publicKeySize*2+8+8)
	out = append(out, node[:]...)
	out = append(out, parent[:]...)
	out, _ = req.encode(out)
	return out
}

func (req *routerSigReq) size() int {
	size := wireSizeUint(req.seq)
	size += wireSizeUint(req.nonce)
	return size
}

func (req *routerSigReq) encode(out []byte) ([]byte, error) {
	start := len(out)
	out = wireAppendUint(out, req.seq)
	out = wireAppendUint(out, req.nonce)
	end := len(out)
	if end-start != req.size() {
		panic("this should never happen")
	}
	return out, nil
}

func (req *routerSigReq) chop(data *[]byte) error {
	var tmp routerSigReq
	orig := *data
	if !wireChopUint(&tmp.seq, &orig) {
		return types.ErrDecode
	} else if !wireChopUint(&tmp.nonce, &orig) {
		return types.ErrDecode
	}
	*req = tmp
	*data = orig
	return nil
}

func (req *routerSigReq) decode(data []byte) error {
	var tmp routerSigReq
	if err := tmp.chop(&data); err != nil {
		return err
	} else if len(data) != 0 {
		return types.ErrDecode
	}
	*req = tmp
	return nil
}

/*****************
 * routerSigRes *
 *****************/

type routerSigRes struct {
	routerSigReq
	port peerPort
	psig signature
}

func (res *routerSigRes) check(node, parent publicKey) bool {
	bs := res.bytesForSig(node, parent)
	return parent.verify(bs, &res.psig)
}

func (res *routerSigRes) bytesForSig(node, parent publicKey) []byte {
	bs := res.routerSigReq.bytesForSig(node, parent)
	bs = wireAppendUint(bs, uint64(res.port))
	return bs
}

func (res *routerSigRes) size() int {
	size := res.routerSigReq.size()
	size += wireSizeUint(uint64(res.port))
	size += len(res.psig)
	return size
}

func (res *routerSigRes) encode(out []byte) ([]byte, error) {
	start := len(out)
	var err error
	out, err = res.routerSigReq.encode(out)
	if err != nil {
		return nil, err
	}
	out = wireAppendUint(out, uint64(res.port))
	out = append(out, res.psig[:]...)
	end := len(out)
	if end-start != res.size() {
		panic("this should never happen")
	}
	return out, nil
}

func (res *routerSigRes) chop(data *[]byte) error {
	orig := *data
	var tmp routerSigRes
	if err := tmp.routerSigReq.chop(&orig); err != nil {
		return err
	} else if !wireChopUint((*uint64)(&tmp.port), &orig) {
		return types.ErrDecode
	} else if !wireChopSlice(tmp.psig[:], &orig) {
		return types.ErrDecode
	}
	*res = tmp
	*data = orig
	return nil
}

func (res *routerSigRes) decode(data []byte) error {
	var tmp routerSigRes
	if err := tmp.chop(&data); err != nil {
		return err
	} else if len(data) != 0 {
		return types.ErrDecode
	}
	*res = tmp
	return nil
}

/*******************
 * routerAnnounce *
 *******************/

type routerAnnounce struct {
	key    publicKey
	parent publicKey
	routerSigRes
	sig signature
}

func (ann *routerAnnounce) check() bool {
	if ann.port == 0 && ann.key != ann.parent {
		return false
	}
	bs := ann.bytesForSig(ann.key, ann.parent)
	return ann.key.verify(bs, &ann.sig) && ann.parent.verify(bs, &ann.psig)
}

func (ann *routerAnnounce) size() int {
	size := len(ann.key)
	size += len(ann.parent)
	size += ann.routerSigRes.size()
	size += len(ann.sig)
	return size
}

func (ann *routerAnnounce) encode(out []byte) ([]byte, error) {
	start := len(out)
	var err error
	out = append(out, ann.key[:]...)
	out = append(out, ann.parent[:]...)
	out, err = ann.routerSigRes.encode(out)
	if err != nil {
		return nil, err
	}
	out = append(out, ann.sig[:]...)
	end := len(out)
	if end-start != ann.size() {
		panic("this should never happen")
	}
	return out, nil
}

func (ann *routerAnnounce) decode(data []byte) error {
	var tmp routerAnnounce
	if !wireChopSlice(tmp.key[:], &data) {
		return types.ErrDecode
	} else if !wireChopSlice(tmp.parent[:], &data) {
		return types.ErrDecode
	} else if err := tmp.routerSigRes.chop(&data); err != nil {
		return err
	} else if !wireChopSlice(tmp.sig[:], &data) {
		return types.ErrDecode
	} else if len(data) != 0 {
		return types.ErrDecode
	}
	*ann = tmp
	return nil
}

/***************
 * routerInfo *
 ***************/

// This is the value stored in a key,value map

type routerInfo struct {
	parent publicKey
	routerSigRes
	sig signature
}

func (info *routerInfo) getAnnounce(key publicKey) *routerAnnounce {
	return &routerAnnounce{
		key:          key,
		parent:       info.parent,
		routerSigRes: info.routerSigRes,
		sig:          info.sig,
	}
}

/****************
 * routerForget *
 ****************/

type routerForget struct {
	routerAnnounce
}
