package de.tudarmstadt.informatik.hostage.sync;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;

import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.widget.ArrayAdapter;
import de.tudarmstadt.informatik.hostage.R;
import de.tudarmstadt.informatik.hostage.logging.UglyDbHelper;

public class BluetoothSync {

	private class ClientThread extends Thread {
		private final BluetoothSocket socket;

		public ClientThread(BluetoothDevice device) {
			BluetoothSocket tmp = null;
			try {
				tmp = device.createRfcommSocketToServiceRecord(serviceUUID);
			} catch (IOException e) {
			}
			socket = tmp;
		}

		/** Will cancel an in-progress connection, and close the socket */
		public void cancel() {
			try {
				socket.close();
			} catch (IOException e) {
			}
		}

		@Override
		public void run() {

			try {
				socket.connect();
			} catch (IOException connectException) {
				// Unable to connect; close the socket and get out
				try {
					socket.close();
				} catch (IOException closeException) {
				}
				return;
			}

			manageConnectedSocket(socket, CLIENT);
		}
	}

	private class CommunicationThread extends Thread {
		private final BluetoothSocket mmSocket;
		private final ObjectInputStream objectInput;
		private final ObjectOutputStream objectOuput;
		private final int identifier;

		public CommunicationThread(BluetoothSocket socket, int identifier) {
			mmSocket = socket;
			ObjectInputStream tmpIn = null;
			ObjectOutputStream tmpOut = null;

			// Get the input and output streams, using temp objects because
			// member streams are final
			try {
				tmpOut = new ObjectOutputStream(socket.getOutputStream());
				tmpIn = new ObjectInputStream(socket.getInputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}

			objectInput = tmpIn;
			objectOuput = tmpOut;
			this.identifier = identifier;
		}

		/* Call this from the main activity to shutdown the connection */
		public void cancel() {
			try {
				mmSocket.close();
			} catch (IOException e) {
			}
		}

		@Override
		public void run() {
			// Keep listening to the InputStream until an exception occurs
			// while (true) {
			try {
				// TODO Ersetze dbh mit Logger
				UglyDbHelper dbh = new UglyDbHelper(context);
				ArrayList<HashMap<String, Object>> localNetworkInformation = dbh
						.getNetworkInformation();
				if (identifier == SERVER) {
					// Read from the InputStream
					ArrayList<HashMap<String, Object>> remoteNetworkInformation = (ArrayList<HashMap<String, Object>>) objectInput
							.readObject();
					;
					dbh.updateNetworkInformation(remoteNetworkInformation);
					objectOuput.writeObject(localNetworkInformation);
				} else {
					objectOuput.writeObject(localNetworkInformation);
					// Read from the InputStream
					ArrayList<HashMap<String, Object>> remoteNetworkInformation = (ArrayList<HashMap<String, Object>>) objectInput
							.readObject();
					dbh.updateNetworkInformation(remoteNetworkInformation);
					mmSocket.close();
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
			// }
		}

		/* Call this from the main activity to send data to the remote device */
		public void write(ArrayList<HashMap<String, Object>> networkInformation) {
			try {
				objectOuput.writeObject(networkInformation);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private class ServerThread extends Thread {
		private final BluetoothServerSocket serverSocket;

		public ServerThread() {
			BluetoothServerSocket tmp = null;
			try {
				tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
						context.getResources().getString(R.string.app_name),
						serviceUUID);
			} catch (IOException e) {
			}
			serverSocket = tmp;
		}

		/** Will cancel the listening socket, and cause the thread to finish */
		public void cancel() {
			try {
				serverSocket.close();
			} catch (IOException e) {
			}
		}

		@Override
		public void run() {
			BluetoothSocket socket = null;
			while (true) {
				try {
					socket = serverSocket.accept();
				} catch (IOException e) {
					e.printStackTrace();
					break;
				}

				if (socket != null) {
					// Do work to manage the connection (in a separate thread)
					manageConnectedSocket(socket, SERVER);
					try {
						serverSocket.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
					break;
				}
			}
		}
	}

	private final UUID serviceUUID;
	private final int SERVER = 0;
	private final int CLIENT = 1;

	private BluetoothAdapter mBluetoothAdapter;
	private Context context;

	private ArrayAdapter<String> arrayAdapter;

	private ServerThread serverThread;

	private AlertDialog ad;

	// Create a BroadcastReceiver for ACTION_FOUND
	private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			// When discovery finds a device
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {
				// Get the BluetoothDevice object from the Intent
				BluetoothDevice device = intent
						.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				// Add the name and address to an array adapter to show in a
				// ListView
				arrayAdapter.add(device.getName() + "\n" + device.getAddress());
				arrayAdapter.notifyDataSetChanged();
			}
		}
	};

	public BluetoothSync(Context context) {
		this.context = context;
		serviceUUID = UUID.fromString(context.getResources().getString(
				R.string.UUID));
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
	}

	public boolean syncData() {
		if (!bluetoothAvaible())
			return false;
		syncDataPassive();
		return syncDataActive();
	}

	private boolean bluetoothAvaible() {
		if (mBluetoothAdapter == null) {
			// Device does not support Bluetooth
			return false;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			Intent enableBtIntent = new Intent(
					BluetoothAdapter.ACTION_REQUEST_ENABLE);
			context.startActivity(enableBtIntent);
			return false;
		}
		return true;
	}

	private void deviceDialog() {
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		// TODO in resources auslagern
		builder.setTitle("Choose Device");
		builder.setAdapter(arrayAdapter, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int position) {
				String deviceInfo = arrayAdapter.getItem(position);
				String mac = deviceInfo.substring(deviceInfo.indexOf("\n") + 1);
				ClientThread clientThread = new ClientThread(mBluetoothAdapter
						.getRemoteDevice(mac));
				clientThread.start();
			}
		});
		// builder.create();
		ad = builder.show();
	}

	private void manageConnectedSocket(BluetoothSocket socket, int identifier) {
		if (identifier == SERVER) {
			ad.dismiss();
		}
		mBluetoothAdapter.cancelDiscovery();
		context.unregisterReceiver(mReceiver);
		CommunicationThread commThread = new CommunicationThread(socket,
				identifier);
		commThread.start();
	}

	// Register the BroadcastReceiver
	private void registerBroadcastReceiver() {
		IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
		context.registerReceiver(mReceiver, filter); // Don't forget to
														// unregister during
														// onDestroy
	}

	private boolean syncDataActive() {
		registerBroadcastReceiver();
		arrayAdapter = new ArrayAdapter<String>(context,
				android.R.layout.simple_list_item_1);
		// Start scanning for devices
		if (!mBluetoothAdapter.startDiscovery())
			return false;

		deviceDialog();
		return true;
	}

	private void syncDataPassive() {
		Intent discoverableIntent = new Intent(
				BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		discoverableIntent.putExtra(
				BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
		context.startActivity(discoverableIntent);

		serverThread = new ServerThread();
		serverThread.start();
	}

}
