package bt;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

import Game.Global;

/**
 * 处理蓝牙连接及设备/服务发现过程的主要类。该类处理事务包括：搜索蓝牙设备； 建立本地蓝牙服务并注册运行；处理远端蓝牙服务，并与之建立连接。
 * 
 * @author DZF
 * @version 1.0.0
 * 
 */
public class NetLayer implements Runnable {

	public final static byte SIGNAL_HANDSHAKE = 0;
	public final static byte SIGNAL_HANDSHAKE_ACK = 1;

	public final static byte SIGNAL_PLAYER_ENTER = 20;
	public final static byte SIGNAL_PLAYER_ENTER_ACK = 21;
	public final static byte SIGNAL_PLAYER_LEAVE = 22;
	public final static byte SIGNAL_PLAYER_LEAVE_ACK = 23;

	public final static byte SIGNAL_READY = 30;
	public final static byte SIGNAL_PLAYER_NET_NUMBER = 31;
	public final static byte SIGNAL_READY_ACK = 32;
	public final static byte SIGNAL_START_PLAY = 33;
	public final static byte SIGNAL_START_PLAY_ACK = 34;

	public final static byte SIGNAL_GAME_MSG = 4;

	public final static byte SIGNAL_TERMINATE = 7;
	public final static byte SIGNAL_TERMINATE_ACK = 8;

	public final static byte SIGNAL_PLAYER_INFO_ACK = 21;
	public final static byte SIGNAL_HOST_INFO = 22;

	public final static byte SIGNAL_SYSTEM_MSG = 3;

	private final static UUID uuid = new UUID(
			"102030405060708090A0B0C0D0E0F010", false);

	private final static int SERVICE_TELEPHONY = 0x400000;

	public boolean done = false;

	public boolean isServer = false;

	public byte myPlayerNumber = -1;

	byte nowPlayerNumber = -1;

	byte maxPlayerNumber = Global.MAX_PLAYER_NUMBER;

	public byte actualPlayerNum = -1;

	byte readyPlayerNum = -1;

	byte mapInfo = -1;

	byte roleInfo = -1;

	public byte netPlayerRole = -1;

	public byte netPlayer2Role = Global.JACK;

	byte[] information = new byte[Global.INFORMATION_LENGTH];

	LocalDevice localDevice = null;

	DiscoveryAgent agent = null;

	StreamConnectionNotifier server;

	BTListener callback = null;

	String localName = "";

	Vector endPoints = new Vector();

	Vector pendingEndPoints = new Vector();

	Hashtable serviceRecordToEndPoint = new Hashtable();

	Object lock = new Object();

	Timer timer = new Timer();

	public NetLayer() {

	}

	public void init(BTListener callback) {

		if (isServer) {
			nowPlayerNumber = 1;
			myPlayerNumber = 1;
			actualPlayerNum = 1;
			readyPlayerNum = 1;
		}
		try {
			this.callback = callback;

			localDevice = LocalDevice.getLocalDevice();

			this.localName = localDevice.getFriendlyName();

			localDevice.setDiscoverable(DiscoveryAgent.GIAC);

			agent = localDevice.getDiscoveryAgent();

			Thread thread = new Thread(this);
			thread.start();
		} catch (BluetoothStateException e) {
			e.printStackTrace();
		}
	}

	public void stopSearch() {
		done = true;
		try {
			server.close();
		} catch (IOException ex) {

		}
	}

	public void disconnect() {
		done = true;
		try {
			server.close();
		} catch (IOException ex) {

		}
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			endpt.putString(NetLayer.SIGNAL_TERMINATE, information);
			endpt.sender.stop();
			endpt.reader.stop();
		}
	}

	public void query() {
		try {
			agent.startInquiry(DiscoveryAgent.GIAC, new Listener());
		} catch (BluetoothStateException e) {
			e.printStackTrace();
		}

	}

	public void setGameInformation() {


	}

	public EndPoint findEndPointByRemoteDevice(RemoteDevice rdev) {
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			if (endpt.remoteDev.equals(rdev)) {
				return endpt;
			}
		}
		return null;
	}

	public EndPoint findEndPointByTransId(int id) {
		for (int i = 0; i < pendingEndPoints.size(); i++) {
			EndPoint endpt = (EndPoint) pendingEndPoints.elementAt(i);
			if (endpt.transId == id) {
				return endpt;
			}
		}
		return null;
	}

	public void sendJoinMsg(byte msg) {
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			if (endpt.remoteDeviceIsHost) {
				// information内容无关
				endpt.putString(msg, information);
				break;
			}
		}
	}

	public void sendPlayerNetNum() {
		byte[] temp = new byte[Global.INFORMATION_LENGTH];
		temp[0] = this.myPlayerNumber;
		System.out.println("!@!@!@!@" + this.myPlayerNumber);
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			if (!endpt.remoteDeviceIsHost)
				endpt.putString(SIGNAL_PLAYER_NET_NUMBER, temp);
		}
	}

	public void sendBeReadyMsg() {
		byte playerNum = 2;
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			if (endpt.inRoom) {
				byte[] s = new byte[Global.INFORMATION_LENGTH];
				endpt.netPlayerNumber = playerNum;
				s[0] = playerNum;
				playerNum++;
				s[1] = this.nowPlayerNumber;
				endpt.putString(SIGNAL_READY, s);
			}
		}
	}

	public void sendPlayMsg() {
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			if (endpt.inRoom) {
				byte[] s = new byte[Global.INFORMATION_LENGTH];
				s[3] = this.actualPlayerNum;
				endpt.putString(SIGNAL_START_PLAY, s);
			}
		}
	}

	public void sendPosition(byte[] position) {
		for (int i = 0; i < endPoints.size(); i++) {
			EndPoint endpt = (EndPoint) endPoints.elementAt(i);
			endpt.putString(SIGNAL_GAME_MSG, position);
		}
	}

	public void cleanupRemoteEndPoint(EndPoint endpt) {
		endpt.reader.stop();
		endpt.sender.stop();
		endPoints.removeElement(endpt);
	}

	public void run() {
		// TODO Auto-generated method stub
		StreamConnection c = null;
		try {
			server = (StreamConnectionNotifier) Connector
					.open("btspp://localhost:" + uuid.toString()
							+ ";name=DreamBubbleApp");
			ServiceRecord rec = localDevice.getRecord(server);
			rec.setAttributeValue(0x0008, new DataElement(DataElement.U_INT_1,
					0xFF));
			rec.setDeviceServiceClasses(SERVICE_TELEPHONY);
		} catch (Exception e) {
			e.printStackTrace();
		}

		while (!done) {
			try {
				c = server.acceptAndOpen();

				RemoteDevice rdev = RemoteDevice.getRemoteDevice(c);
				EndPoint endpt = findEndPointByRemoteDevice(rdev);
				if (endpt != null) {
					// this is a safe guard to assure that this client
					// has not been connected before
				} else {
					// - create a new EndPoint object
					// - initialize the member variables
					// - start the data reader and sender threads.
					endpt = new EndPoint(this, rdev, c);

					Thread t1 = new Thread(endpt.sender);
					t1.start();

					Thread t2 = new Thread(endpt.reader);
					t2.start();

					// add this EndPoint to the active list
					endPoints.addElement(endpt);

					byte[] temp = new byte[Global.INFORMATION_LENGTH];

					if (isServer) {
						temp[0] = Global.SERVER;
						temp[1] = mapInfo;
						temp[2] = roleInfo;
						temp[3] = nowPlayerNumber;
					} else {
						temp[0] = Global.CLIENT;
						temp[2] = roleInfo;
					}

					endpt.putString(SIGNAL_HANDSHAKE, temp);

				}
			} catch (IOException e) {
				e.printStackTrace();

				if (c != null)
					try {
						c.close();
					} catch (IOException e2) {
					}

			} finally {

			}
		}

	}

	class Listener implements DiscoveryListener {

		public void deviceDiscovered(RemoteDevice remoteDevice,
				DeviceClass deviceClass) {
			// TODO Auto-generated method stub
			try {
				EndPoint endpt = new EndPoint(NetLayer.this, remoteDevice, null);
				pendingEndPoints.addElement(endpt);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		public void inquiryCompleted(int transId) {
			// TODO Auto-generated method stub
			timer.schedule(new DoServiceDiscovery(), 100);
		}

		public void serviceSearchCompleted(int tansID, int repCode) {
			// TODO Auto-generated method stub
			for (Enumeration records = serviceRecordToEndPoint.keys(); records
					.hasMoreElements();) {
				try {
					ServiceRecord rec = (ServiceRecord) records.nextElement();
					String url = rec.getConnectionURL(
							ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
					StreamConnection con = (StreamConnection) Connector
							.open(url);
					EndPoint endpt = (EndPoint) serviceRecordToEndPoint
							.get(rec);
					if (endpt != null) {
						endpt.con = con;

						Thread t1 = new Thread(endpt.sender);
						t1.start();

						Thread t2 = new Thread(endpt.reader);
						t2.start();

						endPoints.addElement(endpt);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			serviceRecordToEndPoint.clear();

			synchronized (lock) {
				lock.notifyAll();
			}
		}

		public void servicesDiscovered(int transId, ServiceRecord[] svcRec) {
			// TODO Auto-generated method stub
			try {
				for (int i = 0; i < svcRec.length; i++) {
					EndPoint endpt = findEndPointByTransId(transId);
					serviceRecordToEndPoint.put(svcRec[i], endpt);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	class DoServiceDiscovery extends TimerTask {

		public void run() {
			// TODO Auto-generated method stub
			for (int i = 0; i < pendingEndPoints.size(); i++) {
				EndPoint endpt = (EndPoint) pendingEndPoints.elementAt(i);

				try {
					endpt.transId = agent.searchServices(null,
							new UUID[] { uuid }, endpt.remoteDev,
							new Listener());
					synchronized (lock) {
						try {
							lock.wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				} catch (BluetoothStateException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

			pendingEndPoints.removeAllElements();
		}
	}

}
