package de.tudarmstadt.informatik.hostage.ui;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ToggleButton;
import android.widget.ViewAnimator;
import de.tudarmstadt.informatik.hostage.HoneyService;
import de.tudarmstadt.informatik.hostage.HoneyService.LocalBinder;
import de.tudarmstadt.informatik.hostage.R;
import de.tudarmstadt.informatik.hostage.commons.HelperUtils;
import de.tudarmstadt.informatik.hostage.logging.LogResultReceiver;
import de.tudarmstadt.informatik.hostage.logging.LogResultReceiver.Receiver;
import de.tudarmstadt.informatik.hostage.logging.Logger;
import de.tudarmstadt.informatik.hostage.logging.UglyDbHelper;

/**
 * MainActivity is the central activity for the GUI of the application.
 * MainActivity is launched when the application is first started. It shows the
 * user: <br>
 * - information about the network<br>
 * - light indicators for recorded attacks on each protocol<br>
 * - amount of attacks on each protocols<br>
 * The user can start and stop services.
 * 
 * @author Mihai Plasoianu
 * @author Lars Pandikow
 * @author Wulf Pfeiffer
 */
public class MainActivity extends Activity implements Receiver {
	/**
	 * Flag for root acces. True if phone has root acces, else false.
	 */
	public static boolean isRooted = false;
	/**
	 * Flag for porthack. True if porthack is installed, else false.
	 */
	public static boolean porthackInstalled = false;

	/**
	 * Integer representing a grey light.
	 */
	public static final int LIGHT_GREY = 0x01;
	/**
	 * Integer representing a green light.
	 */
	public static final int LIGHT_GREEN = 0x02;
	/**
	 * Integer representing a red light.
	 */
	public static final int LIGHT_RED = 0x03;
	/**
	 * Integer representing a yellow light.
	 */
	public static final int LIGHT_YELLOW = 0x04;

	public LogResultReceiver logResultReceiver;
	private SharedPreferences connectionInfo;

	private HoneyService mService;
	private boolean serviceBound;

	// variables for the swipe animation
	private ViewAnimator viewAnimator;
	private GestureDetector gestureDetector;
	private Animation animFlipInLR;
	private Animation animFlipOutLR;
	private Animation animFlipInRL;
	private Animation animFlipOutRL;

	private ListView listView;
	private ListViewAdapter adapter;

	private boolean isBssidSeen = false;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		logResultReceiver = new LogResultReceiver(new Handler());
		setContentView(R.layout.activity_main);
		connectionInfo = getSharedPreferences(getString(R.string.connection_info), Context.MODE_PRIVATE);
		
		// Create dynamic view elements
		initViewAnimator();
		initListView();
		// Initialize Class variables
		checkRootAndPorthack();
		startAndBind();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		switch (item.getItemId()) {
		case R.id.action_settings:
			startActivity(new Intent(this, SettingsActivity.class));
			break;
		case R.id.action_about:
			startActivity(new Intent(this, AboutActivity.class));
			break;
		default:
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onStart() {
		super.onStart();
		// Register Broadcast Receiver
		registerReceiver();
		logResultReceiver.setReceiver(this);
		// Bind service if running, else check for connection change and delete
		// sessionData
		if (isServiceRunning()) {
			bindService(getServiceIntent(), mConnection, BIND_AUTO_CREATE);
		} 
		// Update UI
		updateConnectionInfText();
	}

	@Override
	protected void onStop() {
		// Unregister Broadcast Receiver
		unregisterReceiver();
		logResultReceiver.setReceiver(null);
		super.onStop();
	}

	@Override
	protected void onDestroy() {
		// Unbind running service
		if(!mService.hasRunningListeners()){
			stopAndUnbind();
		}
		super.onDestroy();
	}

	@Override
	public void onReceiveResult(int resultCode, Bundle resultData) {
		isBssidSeen = resultData.getBoolean("result");
	}

	/**
	 * Called when User presses on/off button.
	 * 
	 * @param view
	 */
	public void buttonOnOffClick(View view) {
		if (((ToggleButton) view).isChecked()) {
			if (isParanoid()) {
				String[] protocols = getResources().getStringArray(R.array.protocols);
				for(String protocol: protocols){
					mService.startListener(protocol);	
				}						
			} else {
				if(mService.isRunning("SMB")){
					mService.stopListener("SMB");
				}else{
					mService.startListener("SMB");
				}	
			}
		} else {
			mService.stopListeners();
			stopAndUnbind();
		}
	}

	/**
	 * Starts the ViewLog activity, when the Button is pressed.
	 * 
	 * @see ViewLog
	 * @param view
	 *            View elements which triggers the method call.
	 */
	public void showLog(View view) {
		startActivity(new Intent(this, ViewLog.class));
	}

	public void startPlayGround(View view) {
		startActivity(new Intent(this, PlayGroundActivity.class));
	}

	/**
	 * If mobile phone is connected to a wireless network starts the background
	 * service ands binds itself to it. Else notifies the user that service
	 * could not be started.
	 */
	private void startAndBind() {
		startService(getServiceIntent());
		bindService();
	}

	/**
	 * Binds service to Activity
	 * 
	 * @see HoneyService
	 */
	private void bindService() {
		bindService(getServiceIntent(), mConnection, BIND_AUTO_CREATE);
	}

	/**
	 * Stops service and unbinds it.
	 * 
	 * @see HoneyService
	 */
	private void stopAndUnbind() {
		unbindService();
		stopService(getServiceIntent());
	}

	/**
	 * Unbinds service.
	 * 
	 * @see HoneyService
	 */
	private void unbindService() {
		unbindService(mConnection);
	}

	/**
	 * Connection to bind the background service
	 * 
	 * @see HoneyService
	 */
	private ServiceConnection mConnection = new ServiceConnection() {
		/**
		 * After the service is bound, check which has been clicked and start
		 * it.
		 * 
		 * @see android.content.ServiceConnection#onServiceConnected(android.content.ComponentName)
		 */
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mService = ((LocalBinder) service).getService();
			serviceBound = true;
			updateUI();
		}

		/**
		 * After the service is unbound, delete reference.
		 * 
		 * @see android.content.ServiceConnection#onServiceDisconnected(android.content.ComponentName)
		 */
		@Override
		public void onServiceDisconnected(ComponentName name) {
			mService = null;
			serviceBound = false;
		}

	};

	/**
	 * Checks if the phone ist rooted and if porthack is installed. Sets flags
	 * {@link isRooted} and {@link porthackInstalled}
	 */
	private void checkRootAndPorthack() {
		isRooted = false;
		porthackInstalled = false;
		Process p;
		try {
			String found = "Found";
			String notFound = "Not found";
			String command = "[ -f /data/local/p ] && echo " + found
					+ " || echo " + notFound;
			p = Runtime.getRuntime().exec(new String[] { "su", "-c", command });
			BufferedReader in = new BufferedReader(new InputStreamReader(
					p.getInputStream()));
			/*
			 * int av = byte[] b = new byte[av]; if (av != 0) { in.read(b); }
			 */
			String echoResponse = in.readLine();
			Log.i("MainAc", echoResponse);
			if (echoResponse.equals(found)) {
				isRooted = true;
				porthackInstalled = true;
			} else if (echoResponse.equals(notFound)) {
				isRooted = true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		Log.i("MainAc", "Rooted: " + isRooted + " Porthack: "
				+ porthackInstalled);
	}

	/**
	 * Returns an intent to start HoneyService.
	 * 
	 * @return An Intent to start HoneyService
	 */
	private Intent getServiceIntent() {
		return new Intent(this, HoneyService.class);
	}

	/**
	 * Checks if user selected paranoid mode.
	 * 
	 * @return True when paranoid mode is selected, else returns false.
	 */
	private boolean isParanoid() {
		return ((CheckBox) findViewById(R.id.checkBoxParanoid)).isChecked();
	}

	/**
	 * Initializes the ListView. Creating its contents dynamic from protocol
	 * res/values/protocols.xml
	 */
	private void initListView() {
		ArrayList<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
		for (String protocol : getResources().getStringArray(R.array.protocols)) {
			HashMap<String, String> d = new HashMap<String, String>();
			d.put("light", String.valueOf(R.drawable.light_grey));
			d.put("protocol", protocol);
			d.put("connections", "-");
			data.add(d);
		}
		listView = (ListView) findViewById(R.id.listViewProtocols);
		adapter = new ListViewAdapter(getLayoutInflater(), data);
		listView.setAdapter(adapter);
		listView.setOnTouchListener(new OnTouchListener() {

			@Override
			public boolean onTouch(View v, MotionEvent event) {
				return gestureDetector.onTouchEvent(event);
			}

		});
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				String protocolName = (String) ((HashMap<?, ?>) adapter
						.getItem(position)).get("protocol");
				if(mService.isRunning(protocolName)){
					mService.stopListener(protocolName);
				}else{
					mService.startListener(protocolName);
				}				
			}
		});
	}
	
	/**
	 * Checks if a {@link HoneyService} instance is running.
	 * 
	 * @return True if {@link HoneyService} is running, else false.
	 */
	private boolean isServiceRunning() {
		ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
		for (RunningServiceInfo service : manager
				.getRunningServices(Integer.MAX_VALUE)) {
			if (service.service.getClassName().equals(
					HoneyService.class.getName())) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Checks if a {@link HoneyService} instance is running.
	 * 
	 * @return True if {@link HoneyService} is running, else false.
	 */
	private boolean isServiceBound() {
		return serviceBound;
	}

	/**
	 * Register broadcast receiver for custom broadcast.
	 * 
	 * @see #BROADCAST
	 */
	private void registerReceiver() {
		LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
				new IntentFilter(getString(R.string.broadcast)));
	}

	/**
	 * Unregisters broadcast receiver for custom broadcast.
	 * 
	 * @see #BROADCAST
	 */
	private void unregisterReceiver() {
		LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
	}

	/**
	 * Receiver for custom broadcast.
	 * 
	 * @see #BROADCAST
	 */
	private BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			// Update user interface.
			updateUI();
			updateConnectionInfText();
		}
	};

	/**
	 * Updates Information shown by the GUI.
	 */
	private void updateUI() {
		boolean activeListeners = false;
		boolean activeHandlers = false;
		boolean yellowLight = false;
		
		// Check for all protocols if listeners are active and attacks have been
		// recorded
		// Update protocol lights and connection information.
		for (String protocol : getResources().getStringArray(R.array.protocols)) {
			if(isServiceBound()){
				// Check if protocol is active
				if (mService.isRunning(protocol)) {
					activeListeners = true;
					int handlerCount = mService.getNumberOfActiveConnections(protocol);
					// Check if attacks have been recorded in this session.
					if (handlerCount > 0) {
						activeHandlers = true;
						updateProtocolLight(LIGHT_RED, protocol);
						updateProtocolConnections(handlerCount, protocol);
					} else {
						// Check if the bssid of the wireless network has already
						// been recorded as infected.
						Logger.isBssidSeen(getApplicationContext(), protocol,
								HelperUtils.getBSSID(getApplicationContext()),
								logResultReceiver);
						UglyDbHelper dbh = new UglyDbHelper(this);
						if (dbh.bssidSeen(protocol, connectionInfo.getString(getString(R.string.connection_info_bssid), null))) {
							updateProtocolLight(LIGHT_YELLOW, protocol);
							yellowLight = true;
						} else {
							updateProtocolLight(LIGHT_GREEN, protocol);
						}
						updateProtocolConnections(0, protocol);
					}
				} else {
					updateProtocolLight(LIGHT_GREY, protocol);
				}
			}else{
				updateProtocolLight(LIGHT_GREY, protocol);
			}				
		}

		// Update the big attack indicator.
		if (activeListeners) {
			if (activeHandlers) {
				updateStatusLight(LIGHT_RED);
			} else {
				if (yellowLight) {
					updateStatusLight(LIGHT_YELLOW);
				} else {
					updateStatusLight(LIGHT_GREEN);
				}
			}
			((ToggleButton) findViewById(R.id.toggleButtonOnOff))
					.setChecked(true);
			findViewById(R.id.checkBoxParanoid).setEnabled(false);
		} else {
			updateStatusLight(LIGHT_GREY);
			((ToggleButton) findViewById(R.id.toggleButtonOnOff))
					.setChecked(false);
			findViewById(R.id.checkBoxParanoid).setEnabled(true);
		}
	}

	/**
	 * Sets the big light indicator.
	 * 
	 * @param light
	 *            Integer code to set the light color.
	 * @see #LIGHT_GREY
	 * @see #LIGHT_GREEN
	 * @see #LIGHT_RED
	 * @see #LIGHT_YELLOW
	 */
	private void updateStatusLight(int light) {
		switch (light) {
		case LIGHT_GREY:
			((ImageView) findViewById(R.id.imageViewLight))
					.setImageResource(R.drawable.light_grey_large);
			break;
		case LIGHT_GREEN:
			((ImageView) findViewById(R.id.imageViewLight))
					.setImageResource(R.drawable.light_green_large);
			break;
		case LIGHT_RED:
			((ImageView) findViewById(R.id.imageViewLight))
					.setImageResource(R.drawable.light_red_large);
			break;
		case LIGHT_YELLOW:
			((ImageView) findViewById(R.id.imageViewLight))
					.setImageResource(R.drawable.light_yellow_large);
			break;
		}
	}

	/**
	 * Sets the light indicator for a given protocol.
	 * 
	 * @param light
	 *            Integer code to set the light color.
	 * @param protocolName
	 *            Name of the protocol which should be updated.
	 */
	private void updateProtocolLight(int light, String protocolName) {
		for (int i = 0; i < adapter.getCount(); ++i) {
			HashMap<String, String> d = (HashMap<String, String>) adapter
					.getItem(i);
			if (d.get("protocol").equals(protocolName)) {
				switch (light) {
				case LIGHT_GREY:
					d.put("light", String.valueOf(R.drawable.light_grey));
					d.put("connections", "-");
					break;
				case LIGHT_GREEN:
					d.put("light", String.valueOf(R.drawable.light_green));
					break;
				case LIGHT_RED:
					d.put("light", String.valueOf(R.drawable.light_red));
					break;
				case LIGHT_YELLOW:
					d.put("light", String.valueOf(R.drawable.light_yellow));
					break;
				}
			}
		}
		adapter.notifyDataSetChanged();
	}

	/**
	 * Sets the connections count for a given protocol.
	 * 
	 * @param connections
	 *            New value for recorded connections.
	 * @param protocolName
	 *            Name of the protocol which should be updated.
	 */
	private void updateProtocolConnections(int connections, String protocolName) {
		for (int i = 0; i < adapter.getCount(); ++i) {
			HashMap<String, String> d = ((HashMap<String, String>) adapter
					.getItem(i));
			if (d.get("protocol").equals(protocolName)) {
				d.put("connections", String.valueOf(connections));
			}
		}
		adapter.notifyDataSetChanged();
	}

	/**
	 * Gets Information about connection state and updates the GUI.
	 */
	private void updateConnectionInfText() {
		TextView ssidView = (TextView) findViewById(R.id.textViewSSIDValue);
		TextView bssidView = (TextView) findViewById(R.id.textViewBSSIDValue);
		TextView internalIPView = (TextView) findViewById(R.id.textViewInternalIPValue);
		TextView externalIPView = (TextView) findViewById(R.id.textViewExternalIPValue);

//		externalIPView.setText("Loading...");

		// Get connection information
		String ssid = connectionInfo.getString(getString(R.string.connection_info_ssid), null);
		String bssid = connectionInfo.getString(getString(R.string.connection_info_bssid), null);
		String internalIP = connectionInfo.getString(getString(R.string.connection_info_internal_ip), null);
		String externalIP = connectionInfo.getString(getString(R.string.connection_info_external_ip), null);

		// Set text fields
		if (ssid != null)
			ssidView.setText(ssid);
		else
			ssidView.setText("-");

		if (bssid != null)
			bssidView.setText(bssid);
		else
			bssidView.setText("-");

		if (internalIP != null)
			internalIPView.setText(internalIP);
		else
			internalIPView.setText("-");
		
		if (externalIP != null)
			externalIPView.setText(externalIP);
		else
			externalIPView.setText("-");
	}

	/* ############# Help functions for animation ################## */

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return gestureDetector.onTouchEvent(event);
	}

	/**
	 * Initializes variables for screen animation
	 */
	private void initViewAnimator() {
		viewAnimator = (ViewAnimator) findViewById(R.id.viewAnimator);
		gestureDetector = new GestureDetector(this, simpleOnGestureListener);

		animFlipInLR = AnimationUtils.loadAnimation(this,
				R.anim.in_left_to_right);
		animFlipOutLR = AnimationUtils.loadAnimation(this,
				R.anim.out_left_to_right);
		animFlipInRL = AnimationUtils.loadAnimation(this,
				R.anim.in_right_to_left);
		animFlipOutRL = AnimationUtils.loadAnimation(this,
				R.anim.out_right_to_left);
	}

	/**
	 * Called when a swipe to the Left is registered.
	 */
	private void swipeRightToLeft() {
		if (viewAnimator.getDisplayedChild() == 0) {
			viewAnimator.setInAnimation(animFlipInRL);
			viewAnimator.setOutAnimation(animFlipOutRL);
			viewAnimator.setDisplayedChild(1);
		}
	}

	/**
	 * Called when a swipe to the Right is registered.
	 */
	private void swipeLeftToRight() {
		if (viewAnimator.getDisplayedChild() == 1) {
			viewAnimator.setInAnimation(animFlipInLR);
			viewAnimator.setOutAnimation(animFlipOutLR);
			viewAnimator.setDisplayedChild(0);
		}
	}

	SimpleOnGestureListener simpleOnGestureListener = new SimpleOnGestureListener() {
		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
				float velocityY) {
			float sensitvity = 50;
			if ((e1.getX() - e2.getX()) > sensitvity) {
				swipeRightToLeft();
			} else if ((e2.getX() - e1.getX()) > sensitvity) {
				swipeLeftToRight();
			}

			return true;
		}
	};
	
}
