mport java.net.InetAddress;

import java.net.InetSocketAddress;

import java.net.NetworkInterface;

import java.net.SocketException;

import java.net.URI;

import java.net.URISyntaxException;

import java.util.Enumeration;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

import java.util.concurrent.atomic.AtomicInteger;


import org.jboss.netty.bootstrap.ConnectionlessBootstrap;

import org.jboss.netty.channel.AdaptiveReceiveBufferSizePredictor;

import org.jboss.netty.channel.Channel;

import org.jboss.netty.channel.ChannelFuture;

import org.jboss.netty.channel.ChannelFutureListener;

import org.jboss.netty.channel.FixedReceiveBufferSizePredictorFactory;

import org.jboss.netty.channel.socket.DatagramChannel;

import org.jboss.netty.channel.socket.DatagramChannelFactory;

import org.jboss.netty.channel.socket.oio.OioDatagramChannelFactory;

import org.jboss.netty.util.AlarmManagerTimer;

import org.jboss.netty.util.AlarmManagerTimerFactory;


import com.lenovo.lsf.push.log.PushLog;

import com.lenovo.lsf.push.log.PushLog.LEVEL;

import com.lenovo.lsf.push.net.handler.PushUDPHandler;

import com.lenovo.lsf.push.net.pipeline.PushUDPPipelineFactory;

import com.lenovo.lsf.push.net.protobuf.PushUDPCommandProtos.COMMAND_TYPE;

import com.lenovo.lsf.push.net.protobuf.PushUDPCommandProtos.PUSH_UDP_COMMAND;

import com.lenovo.lsf.push.net.protobuf.PushUDPCommandProtos.PUSH_UDP_COMMAND.TTL_INIT;

import com.lenovo.lsf.push.net.protobuf.PushUDPCommandProtos.PUSH_UDP_COMMAND.TTL_INIT.Builder;

import com.lenovo.lsf.push.util.DeviceUtil;

import com.lenovo.lsf.push.util.PushWakeLock;

import com.lenovo.lsf.sdac.SDACDeviceInfo;


import android.content.Context;

import android.content.Intent;

import android.util.Log;


public class PushMessageUDPImpl extends PushDAONetAware implements IPushMessage {


private Context context;

private static ConnectionlessBootstrap bootstrap;

private ChannelFuture channelFuture;

private static DatagramChannel databramChannel;

private String lastNetType="";

private String lastIPAddress="";

private String HOST;

private int PORT;

private String requestURI;

public static final String PUSH_UDP_WAKE_LOCK = "PUSH_UDP_WAKE_LOCK";

public static final int INIT_UDP_FAIL_COUNT = 0;

private int udpFailCount = INIT_UDP_FAIL_COUNT;


public static final int MAX_UDP_INIT_COUNT = 5;

public static String last_local_ip = null;

public static int ttl = 25;

private PushMessageUDPDelayRetryProxy delayProxy;

private PushUDPIntervalTunningManager tunningManager;

private AlarmManagerTimerFactory timerFactory;

//用来生成唯一的PushUDPCommand id

private static AtomicInteger requstCode = new AtomicInteger(0);

//id max

private final int MAX_TIMER = 256;

private boolean isTTLInitRunning = false;

private boolean udpInitialized = false;




public PushMessageUDPImpl(Context context) {

super(context);

// TODO Auto-generated constructor stub

this.context = context;

delayProxy = new PushMessageUDPDelayRetryProxy();

tunningManager = new PushUDPIntervalTunningManager();

timerFactory = new AlarmManagerTimerFactory(PushService.ACTION_INTERNAL_UDP_ALARM_TIMER);


}



public int getUdpFailCount() {

return udpFailCount;

}


public void setUdpFailCount(int udpFailCount) {

this.udpFailCount = udpFailCount;

}



@Override

public void resetDayPollCount() {

// TODO Auto-generated method stub


}


@Override

public void resetFailCount() {

// TODO Auto-generated method stub

udpFailCount = INIT_UDP_FAIL_COUNT;

}





@Override

public void online() {

// TODO Auto-generated method stub

start();

}


@Override

public void offline() {

// TODO Auto-generated method stub

stop();

}


public boolean isTTLInitRunning() {

return isTTLInitRunning;

}


public void setTTLInitRunning(boolean isTTLInitRunning) {

this.isTTLInitRunning = isTTLInitRunning;

}



@Override

public void start() {

// TODO Auto-generated method stub

if(isSwitchOn() && isNetAvailable() && !isEmpty()){


String currentNetType = getNetType();

String currentIPAddress = getIpAddress();



PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.start", "lastNetType:"+lastNetType+", currentNetType:"+currentNetType);

PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.start", "lastIPAddress:"+lastIPAddress+", currentIPAddress:"+currentIPAddress);



lastNetType = currentNetType;

lastIPAddress = currentIPAddress;



requestURI = getPushUDPRequestUrl();



if(requestURI != null && requestURI.indexOf(":") != -1) {

HOST = requestURI.substring(0, requestURI.indexOf(":"));

String portStr = requestURI.substring(requestURI.indexOf(":") + 1, requestURI.length());

try{

PORT = Integer.parseInt(portStr);

} catch (NumberFormatException e) {

e.printStackTrace();

}

PushLog.log(context, PushLog.LEVEL.INFO, "PushMessageUDPImpl.start",

"HOST:" + HOST+"PORT:"+PORT);

}






/*

if (currentIPAddress != null

&& !currentIPAddress.equals(lastIPAddress)) {


initTTL();


}

*/

if(!isTTLInitRunning()) {

initTTL();

}



}else{

Intent i = PushService.newIntent(context,PushService.ACTION_INTERNAL_UDP_STOP_ALL);

//context.sendBroadcast(i);

i = PushIntentAware.awareIntent(context, i);

context.startService(i);

}

}



void initTTL() {

setTTLInitRunning(true);




setUdpInitialized(false);



PushWakeLock.acquire(context, PUSH_UDP_WAKE_LOCK, 30);

DatagramChannelFactory f = new OioDatagramChannelFactory(

Executors.newCachedThreadPool());

bootstrap = new ConnectionlessBootstrap(f);


// Configure the pipeline factory.

bootstrap.setPipelineFactory(new PushUDPPipelineFactory(

context,timerFactory.getNewAlarmManagerTimer(context), this));


// Enable broadcast

bootstrap.setOption("broadcast", "false");

bootstrap.setOption("receiveBufferSizePredictorFactory",

new FixedReceiveBufferSizePredictorFactory(102400));


databramChannel = (DatagramChannel) bootstrap

.bind(new InetSocketAddress(0));



write();

}



void write() {

// write TTL_INIT to server

PUSH_UDP_COMMAND command = getInitTTLCommand(getUDPPushCommandId());

PushLog.log(context, PushLog.LEVEL.INFO,

"PushMessageUDPImpl.write", "send ttl init to server>>>>>>>>>>>>ttlInit:"+command.getTtlInit().getTtl());

channelFuture = databramChannel.write(command,

new InetSocketAddress(HOST,PORT));





channelFuture.addListener(new ChannelFutureListener(){


@Override

public void operationComplete(ChannelFuture future)

throws Exception {

// TODO Auto-generated method stub

if (!future.isSuccess()) {

timerFactory.destroyAlarmManagerTimer(context);

setTTLInitRunning(false);

if(future != null){

Channel channel = future.getChannel();

if(channel != null){

channel.close();

// Wait for the server to close the connection.

channel.getCloseFuture().awaitUninterruptibly();

}

}


PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.sendTTLInitRequest", "connect fail begin to release wake lock!!!");

PushWakeLock.release(context,PUSH_UDP_WAKE_LOCK);



}

}



});

}



public PUSH_UDP_COMMAND getInitTTLCommand(int id){

// build TTL_INIT command

Builder builder = TTL_INIT.newBuilder();

String st = getSt();

builder.setSt(st==null ? "":st);

builder.setTtl(ttl);

try{

String netType = DeviceUtil.getNetType(context);

if(netType != null && !netType.equals("")) {

if(netType.equals("wifi"))

builder.setNetType(DeviceUtil.WIFI);

else if(netType.equals("mobile"))

builder.setNetType(DeviceUtil.MOBILE);

}

} catch (Exception e){

e.printStackTrace();

}

try{

String optCode = SDACDeviceInfo.getInstance().getSdacInfo(context)

.getSystemID();

if(optCode != null && !optCode.equals("")) {

int code = Integer.parseInt(optCode);

builder.setOptCode(code);

}

} catch (Exception e){

e.printStackTrace();

}

try{

String cellId = DeviceUtil.getCellId(context);

if(cellId != null && !cellId.equals("")) {

int cId = Integer.parseInt(cellId);

builder.setNetType(cId);

}



} catch (Exception e){

e.printStackTrace();

}



TTL_INIT ttlInit = builder.build();

PUSH_UDP_COMMAND command = PUSH_UDP_COMMAND.newBuilder().setId(id)

.setType(COMMAND_TYPE.TTL_INIT).setVer(PushUDPHandler.COMMAND_VERSION).setTtlInit(ttlInit).build();

return command;

}



@Override

public void stop() {

// TODO Auto-generated method stub

if(isTTLInitRunning()) {

delayProxy.cancelUDPPushRetryAlarm(context);



setTTLInitRunning(false);

resetFailCount();

PushLog.log(context, PushLog.LEVEL.INFO, "PushMessageUDPImpl.stop",

"PushMessageUDPImpl.stop");

timerFactory.destroyAlarmManagerTimer(context);

if(channelFuture != null){

Channel channel = channelFuture.getChannel();

if(channel != null){

PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.stop", "channel has been closed by future !!!");

channel.close();

// Wait for the server to close the connection.

channel.getCloseFuture().awaitUninterruptibly();

}

}



}

}


@Override

public void udpAvaliable() {

// TODO Auto-generated method stub


}


@Override

public void udpUnAvaliable() {

// TODO Auto-generated method stub


}


@Override

public void expire(int requestCode) {

// TODO Auto-generated method stub

AlarmManagerTimer timer = timerFactory.getAlarmManagerTimer(context);



// TODO Auto-generated method stub

if(timer != null){

timer.expire(requestCode);

}else{



PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.expire()", "push service instance has been recreated, restart initTTL !!!");


setTTLInitRunning(false);

if(channelFuture != null){

Channel channel = channelFuture.getChannel();

if(channel != null){

channel.close();

// Wait for the server to close the connection.

channel.getCloseFuture().awaitUninterruptibly();

}

}





Intent i = PushService.newIntent(context,PushService.ACTION_INTERNAL_UDP_START_ALL);

//context.sendBroadcast(i);

i = PushIntentAware.awareIntent(context, i);

context.startService(i);

}

}


@Override

public void switchOn() {

// TODO Auto-generated method stub

setSwitch(context, true);

}


@Override

public void switchOff() {

// TODO Auto-generated method stub

setSwitch(context, false);

}



public AlarmManagerTimerFactory getTimerFactory() {

return timerFactory;

}


public void setTimerFactory(AlarmManagerTimerFactory timerFactory) {

this.timerFactory = timerFactory;

}


public PushMessageUDPDelayRetryProxy getDelayProxy() {

return delayProxy;

}


public void setDelayProxy(PushMessageUDPDelayRetryProxy delayProxy) {

this.delayProxy = delayProxy;

}



public PushUDPIntervalTunningManager getTunningManager() {

return tunningManager;

}


public void setTunningManager(PushUDPIntervalTunningManager tunningManager) {

this.tunningManager = tunningManager;

}



public boolean isUdpInitialized() {

return udpInitialized;

}


public void setUdpInitialized(boolean udpInitialized) {

this.udpInitialized = udpInitialized;

}




private String getPushUDPRequestUrl() {


ExecutorService executor = Executors.newSingleThreadExecutor();

Future<String> future = (Future<String>) executor

.submit(new Callable<String>() {


@Override

public String call() throws Exception {

String addr = getServerAddr("rpsb002");

if(addr == null) {

PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.getPushUDPRequestUrl", "get server address failed");

}else{

PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.getPushUDPRequestUrl", "url:"+addr);

}



StringBuilder sb = new StringBuilder(addr==null ? "":addr);


PushLog.log(context, LEVEL.INFO, "PushMessageUDPImpl.getPushUDPRequestUrl", "command line is:" + sb.toString());



return sb.toString();

}


});


try {

String pushURI = future.get();

return pushURI;

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}


return null;

}




//生成全局唯一UDPPushCommand id

public int getUDPPushCommandId() {

int code = requstCode.getAndIncrement();

if (code > MAX_TIMER) {

code = 0;

requstCode = new AtomicInteger(0);

}

return code;

}


@Override

public void resetUdpAvaliable() {

// TODO Auto-generated method stub



}



}