package me.pjq.pushup.lan;
import android.text.TextUtils;
import android.util.Log;
import me.pjq.pushup.AppPreference;
import me.pjq.pushup.MyApplication;
import me.pjq.pushup.utils.Utils;
import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.View;
import org.jgroups.protocols.BARRIER;
import org.jgroups.protocols.FD_ALL;
import org.jgroups.protocols.FD_SOCK;
import org.jgroups.protocols.FRAG2;
import org.jgroups.protocols.MERGE2;
import org.jgroups.protocols.MFC;
import org.jgroups.protocols.PING;
import org.jgroups.protocols.UDP;
import org.jgroups.protocols.UFC;
import org.jgroups.protocols.UNICAST2;
import org.jgroups.protocols.VERIFY_SUSPECT;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK;
import org.jgroups.protocols.pbcast.STABLE;
import org.jgroups.stack.ProtocolStack;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Created by kicoolzhang on 11/12/13.
*/
public class PeersMgr {
private static final String TAG = "PeersMgr";
private static final String CHAT_CLUSTER = "WeUp";
private static final int DELAY_TIME = 1000 * 5;
JChannel channel;
String userName;
boolean inLoop = false;
String localIpAddress;
List<Address> peersIpAddress = new ArrayList<Address>();
public Map<String, LanPlayer> getPeers() {
return peers;
}
Map<String, LanPlayer> peers = new HashMap<String, LanPlayer>();
WifiNetworkHelper networkHelper;
ExecutorService senderExecutorService = Executors.newSingleThreadExecutor();
ExecutorService controlerExecutorService = Executors.newSingleThreadExecutor();
//TODO:
// *. a better peer id
// *. peers list
// *. network state
// *. session id for different group in a same localnetwork, maybe use different CHAT_CLUSTER?
// *. Sync time
// *. Sync action
public PeersMgr(WifiNetworkHelper networkHelper) {
this.networkHelper = networkHelper;
userName = "U/";
}
public void start() {
inLoop = true;
try {
initChannel();
} catch (Exception e) {
e.printStackTrace();
return;
}
controlerExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
channel.connect(CHAT_CLUSTER);
} catch (Exception e) {
e.printStackTrace();
}
}
});
controlerExecutorService.execute(new Runnable() {
@Override
public void run() {
loop();
}
});
}
private void initChannel() throws Exception {
WifiNetworkHelper.WifiNetworkInfo wifiInfo = networkHelper.getWifiInfo();
localIpAddress = wifiInfo.getWifiIpAddress();
String address = initUsername();
channel = new JChannel(false);
channel.setName(address);
ProtocolStack stack = new ProtocolStack();
channel.setProtocolStack(stack);
stack.addProtocol(new UDP().setValue("bind_addr", InetAddress.getByName(localIpAddress)))
.addProtocol(new PING())
.addProtocol(new MERGE2())
.addProtocol(new FD_SOCK())
.addProtocol(new FD_ALL().setValue("timeout", 12000).setValue("interval", 3000))
.addProtocol(new VERIFY_SUSPECT())
.addProtocol(new BARRIER())
.addProtocol(new NAKACK())
.addProtocol(new UNICAST2())
.addProtocol(new STABLE())
.addProtocol(new GMS())
.addProtocol(new UFC())
.addProtocol(new MFC())
.addProtocol(new FRAG2());
stack.init();
channel.setReceiver(new Receiver());
}
private String initUsername() {
String name = AppPreference.getInstance(MyApplication.getContext()).getLoginName();
if (!TextUtils.isEmpty(name)) {
userName = name;
} else {
userName = Utils.getDeviceModel();
}
return userName;
}
private void loop() {
while (inLoop) {
if (channel.isConnected()) {
Date data = new Date();
sendNtp(data.getTime());
try {
Thread.sleep(DELAY_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
protected void sendMessage(final String message) {
senderExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
String line = "[" + userName + "] " + message;
Message msg = new Message(null, null, line);
channel.send(msg);
Log.v(TAG, "Send: -> " + line);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public void sendCount(final int count) {
String line = "COUNT:" + count;
sendMessage(line);
}
public void sendNtp(final long time) {
String line = "NTP:" + time;
sendMessage(line);
}
public void stop() {
inLoop = false;
controlerExecutorService.execute(new Runnable() {
@Override
public void run() {
channel.close();
}
});
}
public void restart() {
inLoop = false;
controlerExecutorService.execute(new Runnable() {
@Override
public void run() {
channel.close();
start();
// inLoop = true;
// try {
// channel.connect(CHAT_CLUSTER);
// } catch (Exception e) {
// e.printStackTrace();
// }
}
});
}
protected class Receiver extends ReceiverAdapter {
@Override
public void getState(OutputStream output) throws Exception {
super.getState(output);
}
@Override
public void setState(InputStream input) throws Exception {
super.setState(input);
}
@Override
public void suspect(Address mbr) {
super.suspect(mbr);
}
@Override
public void block() {
super.block();
}
@Override
public void unblock() {
super.unblock();
}
@Override
public void viewAccepted(View new_view) {
Log.i(TAG, "** view: " + new_view);
int peersNumber = new_view.size();
if (peersNumber > 1) {
List<Address> members = new_view.getMembers();
peersIpAddress.clear();
peers.clear();
for (Address a : members) {
String addr = ((Object) a).toString();
Log.i(TAG, "members:" + addr);
// if (!addr.equalsIgnoreCase(localIpAddress)) {
peersIpAddress.add(a);
LanPlayer lanPlayer = new LanPlayer();
lanPlayer.setIp(addr);
lanPlayer.setUsername(channel.getName(a));
lanPlayer.setId(channel.getName(a));
peers.put(addr, lanPlayer);
// }
}
}
LanUtils.sendUpdatePlayerInfoMsg();
}
@Override
public void receive(Message msg) {
String srcAddr = ((Object) msg.getSrc()).toString();
if (srcAddr.equalsIgnoreCase(localIpAddress)) {
return;
}
Log.v(TAG, "Recv: -> " + msg.getObject());
String line = msg.getObject().toString();
if (line.contains("COUNT")) {
String messages[] = line.split(":");
int count = Integer.valueOf(messages[1]);
Log.i(TAG, "COUNT: -> " + count);
LanPlayer lanPlayer = peers.get(srcAddr);
if (null != lanPlayer) {
lanPlayer.setScore(messages[1]);
}
LanUtils.sendUpdatePlayerInfoMsg();
} else if (line.contains("NTP")) {
String messages[] = line.split(":");
long time = Long.valueOf(messages[1]);
Log.i(TAG, "NTP: -> " + time);
LanPlayer lanPlayer = peers.get(srcAddr);
if (null != lanPlayer) {
String score = lanPlayer.getScore();
if (score == null || score.length() == 0) score = "0";
int fake = Integer.valueOf(score);
lanPlayer.setScore(Integer.valueOf(fake).toString());
Log.i(TAG, "fake: -> " + srcAddr + ":" + fake);
}
LanUtils.sendUpdatePlayerInfoMsg();
}
}
}
}