;(function(){

	/* UNBUILD */
	var root;
	if(typeof window !== "undefined"){ root = window }
	if(typeof global !== "undefined"){ root = global }
	root = root || {};
	var console = root.console || {log: function(){}};
	function USE(arg, req){
		return req? require(arg) : arg.slice? USE[R(arg)] : function(mod, path){
			arg(mod = {exports: {}});
			USE[R(path)] = mod.exports;
		}
		function R(p){
			return p.split('/').slice(-1).toString().replace('.js','');
		}
	}
	if(typeof module !== "undefined"){ var common = module }
	/* UNBUILD */

	;USE(function(module){
		if(typeof window !== "undefined"){ module.window = window }
		var tmp = module.window || module;
		var AXE = tmp.AXE || function(){};

		if(AXE.window = module.window){ try{
			AXE.window.AXE = AXE;
			tmp = document.createEvent('CustomEvent');
			tmp.initCustomEvent('extension', false, false, {type: "AXE"});
			(window.dispatchEvent || window.fireEvent)(tmp);
			window.postMessage({type: "AXE"}, '*');
		} catch(e){} }

		try{ if(typeof common !== "undefined"){ common.exports = AXE } }catch(e){}
		module.exports = AXE;
	})(USE, './root');
  
	;USE(function(module){

		var AXE = USE('./root'), Gun = (AXE.window||{}).Gun || USE('./gun', 1);
		(Gun.AXE = AXE).GUN = AXE.Gun = Gun;
		Gun.on('opt', function(at){
			if(!at.axe){
				at.axe = {};
				var peers = at.opt.peers, tmp;
				// 1. If any remembered peers or from last cache or extension
				// 2. Fallback to use hard coded peers from dApp
				// 3. Or any offered peers.
				//if(Gun.obj.empty(p)){
				//  Gun.obj.map(['http://localhost:8765/gun'/*, 'https://guntest.herokuapp.com/gun'*/], function(url){
				//    p[url] = {url: url, axe: {}};
				//  });
				//}
				// Our current hypothesis is that it is most optimal
				// to take peers in a common network, and align
				// them in a line, where you only have left and right
				// peers, so messages propagate left and right in
				// a linear manner with reduced overlap, and
				// with one common superpeer (with ready failovers)
				// in case the p2p linear latency is high.
				// Or there could be plenty of other better options.
				console.log("axe");

				function verify(dht, msg, send, at) {
					var puts = Object.keys(msg.put);
					var soul = puts[0]; /// TODO: verify all souls in puts. Copy the msg only with subscribed souls?
					var subs = dht(soul);
// 					console.log('[AXE] VERIFY soul: %s, subs: %s, Peers: %s, msg: ', soul, subs, Object.keys(peers), msg);
					if (!subs) { return; }
					var tmp = [];
					Gun.obj.map(subs.split(','), function(pid) {
						if (pid in peers) {
							tmp.push(pid);
// 							console.log('[AXE] SEND TO >>>>> ', pid, msg.put.bob || msg.put);
							send(msg, peers[pid]);
						}
					});
					/// Only connected peers in the tmp array.
					if (at.on.opt.super) {
						dht(soul, tmp.join(','));
					}
				}

				var Rad = (Gun.window||{}).Radix || USE('./lib/radix', 1);
				at.opt.dht = Rad();
				at.on('in', input/*USE('./lib/super', 1)*/, at);
// 				at.on('out', function(msg, a) {
// 					this.to.next(msg);
// 					console.log('[AXE] out:', msg, a);
// 				}, at);
				if(at.opt.super){
					AXE.say = function(msg, send, at) {
						if (msg.rtc) {
// 							console.log('[AXE] MSG WEBRTC: ', msg.rtc);
							if (msg.rtc.to) {
								/// Send announce to one peer only if the msg have 'to' attr
								var peer = (at.on.opt.peers) ? at.on.opt.peers[msg.rtc.to] : null;
// 								if (peer) { at.on.opt.mesh.say(msg, peer); }
								if (peer) { send(msg, peer); }
								return;
							}
						}
						if (!msg.put) { send(msg); return; }
						//console.log('AXE HOOK!! ', msg);
						verify(at.on.opt.dht, msg, send, at);
					};
				} else {
					AXE.say = function(msg, send, at) {
						if (msg.rtc) {
// 							console.log('[AXE] MSG WEBRTC: ', msg.rtc);
						}
						if (!msg.put) { send(msg); return; }
						verify(at.on.opt.dht, msg, send, at);
						/// Always send to superpeers?
						Gun.obj.map(at.on.opt.peers, function(peer) {
							if (peer.url) {
// 								console.log('SEND TO SUPERPEER', msg);
								send(msg, peer);
							}
						});
					};
					var connections = 0;
					at.on('hi', function(opt) {
						this.to.next(opt);
						//console.log('AXE PEER [HI]', new Date(), opt);
						connections++;
						/// The first connection don't need to resubscribe the nodes.
						if (connections === 1) { return; }
						/// Resubscribe all nodes.
						setTimeout(function() {
							var souls = Object.keys(at.graph);
							for (var i=0; i < souls.length; ++i) {
								//at.gun.get(souls[i]).off();
								at.next[souls[i]].ack = 0;
								at.gun.get(souls[i]).once(function(){});
							}
						//location.reload();
						}, 500);
					}, at);
				}
			}
			this.to.next(at); // make sure to call the "next" middleware adapter.
		});
		function joindht(dht, soul, pids) {
			if (!pids || !soul || !dht) { return; }
			var subs = dht(soul);
			var tmp = subs ? subs.split(',') : [];
			Gun.obj.map(pids.split(','), function(pid) {
				if (pid && tmp.indexOf(pid) === -1) { tmp.push(pid); }
			});
			tmp = tmp.join(',');
			dht(soul, tmp);
			return tmp;
		}
		function input(msg){
// 			console.log('[AXE] input: ', msg);
			var at = this.as, to = this.to, peer = (msg._||{}).via;
			var opt = at.opt;
			var dht = opt.dht;
			var get = msg.get, soul, key;
			if(peer && get){
				if(soul = get['#']){
					if(key = get['.']){

					} else {

					}
					if (!peer.id) {console.log('[*** WARN] no peer.id %s', soul);}
					var pids = joindht(dht, soul, peer.id);
					if (pids) {
							var dht = {};
							dht[soul] = pids;
							at.opt.mesh.say({dht:dht}, opt.peers[peer.id]);
					}
				}
			}
			to.next(msg);

			if (opt.rtc && msg.dht) {
				Gun.obj.map(msg.dht, function(pids, soul) {
					dht(soul, pids);
					Gun.obj.map(pids.split(','), function(pid) {
						/// TODO: here we can put an algorithm of who must connect?
						if (!pid || pid in opt.peers || pid === opt.pid || opt.announce[pid]) { return; }
							opt.announce[pid] = true; /// To try only one connection to the same peer.
							opt.announce(pid);
					});
				});
			}
		}
		module.exports = AXE;
	})(USE, './axe');
}());