package net.screenfreeze.deskcon;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Formatter;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class DesktopHostsActivity extends Activity {
	private DesktopHostsDBHelper dbhelper;
	private AuthenticationManager authenticationmanager;
	private ListView hostslistview;
	private static DiscoveryTask responseserver;

	@SuppressLint("NewApi")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.desktop_hosts);
		
		dbhelper = new DesktopHostsDBHelper(this);
		authenticationmanager = new AuthenticationManager(this);
		
		ImageView addButton = (ImageView) findViewById(R.id.hostsaddimageView);
		hostslistview = (ListView) findViewById(R.id.hostslistView);
		
		addButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				showAddHostDialog();				
			}
		});
		
		loadHostlist();
		//testreaddb();				
	}
	
	@Override
	protected void onDestroy() {
		dbhelper.close();
		super.onDestroy();
	}

	private void loadHostlist() {
		hostslistview.removeAllViewsInLayout();
		HostsAdapter ha = new HostsAdapter(this, dbhelper.getAllCursor());
		hostslistview.setAdapter(ha);
		hostslistview.setOnItemLongClickListener(new OnItemLongClickListener() {

			@Override
			public boolean onItemLongClick(AdapterView<?> arg0, View parent,
					int pos, long id) {
				Cursor cursor = dbhelper.getHostByIdCursor(id);
				String ip = cursor.getString(2);
				int port = cursor.getInt(3);
				String wifi = cursor.getString(6);
				cursor.close();

				showEditHostDialog(id, ip, port, wifi);

				return false;
			}
		});
		hostslistview.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> arg0, View parent, int pos,
					long id) {
				Cursor cursor = dbhelper.getHostByIdCursor(id);
				String host = cursor.getString(2);
				int port = cursor.getInt(3);
				cursor.close();
				
				Intent i = new Intent(getApplicationContext(), StatusUpdateService.class);
	    		i.putExtra("commandtype", "PING");
	    		i.putExtra("message", "");
	    		i.putExtra("host", host);
	    		i.putExtra("port", port);
	    		startService(i);
	    	    Toast pingmsg = Toast.makeText(getApplicationContext(), 
	            		"send Ping", Toast.LENGTH_SHORT);
	    	    pingmsg.show();
			}
		});
	}
	
	@SuppressWarnings("unused")
	private void testreaddb() {
		Cursor cursor = dbhelper.getHostsOnWifiCursor("<unknown ssid>");
		System.out.println(cursor.getCount());

        if (cursor.moveToFirst()) {
            do {                
                System.out.println(cursor.getLong(0));
                System.out.println(cursor.getString(2));
                System.out.println(cursor.getInt(3));
                //System.out.println(cursor.getString(6));
            } while (cursor.moveToNext());
        }
	}
	
	private void showAddHostDialog() {
		LayoutInflater li = getLayoutInflater();
		View addhostView = li.inflate(R.layout.add_host_dialog, null);
		
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
		alertDialogBuilder.setView(addhostView);
		alertDialogBuilder.setTitle("Pair with Desktop");		
		
		
		alertDialogBuilder.setCancelable(true);
		final EditText ipedittext = (EditText) addhostView.findViewById(R.id.newipeditText);
		final EditText portedittext = (EditText) addhostView.findViewById(R.id.newporteditText);
		final CheckBox wifilockcheckbox = (CheckBox) addhostView.findViewById(R.id.wifilockcheckBox);
		ListView foundHostsListView = (ListView) addhostView.findViewById(R.id.foundlistView);
		ArrayList<DesktopHost> as = new ArrayList<DesktopHost>();
		
		//as.add(new DesktopHost("Hans", "192.168.55.66", 34553, 51674));
		final DiscoveredHostsAdapter dha = new DiscoveredHostsAdapter(getApplicationContext(), as);
		
		responseserver = new DiscoveryTask(dha);
		
		final String wifi = getWifiSSID();
		wifilockcheckbox.setText("only on "+wifi);		
		
		foundHostsListView.setAdapter(dha);
		foundHostsListView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				DesktopHost desk = dha.getItem(position);
				
	        	if (wifilockcheckbox.isChecked()) {
	        		authenticationmanager.pairWithHost(desk.ip, desk.port, wifi, new OnPairCallback());
	        	}
	        	else {
	        		authenticationmanager.pairWithHost(desk.ip, desk.port, "", new OnPairCallback());
	        	}
	        	responseserver.stopServer();
			}
		});
		
		alertDialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				String ip = ipedittext.getText().toString();
				int port = Integer.parseInt(portedittext.getText().toString());   
		        
	        	if (wifilockcheckbox.isChecked()) {
	        		authenticationmanager.pairWithHost(ip, port, wifi, new OnPairCallback());
	        	}
	        	else {
	        		authenticationmanager.pairWithHost(ip, port, "", new OnPairCallback());
	        	}
	        	
	        	responseserver.cancel(true);
				dialog.cancel();					
			}
		});
		
		alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				responseserver.stopServer();
				dialog.cancel();				
			}
		});	
		
	    responseserver.execute();
		
		AlertDialog alertDialog = alertDialogBuilder.create();
		alertDialog.show();		
	}
	
	// Edit a Host
	private void showEditHostDialog(final long id, String ip, int port, String wifi) {
		LayoutInflater li = getLayoutInflater();
		View addhostView = li.inflate(R.layout.add_host_dialog, null);
		
		AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
		alertDialogBuilder.setView(addhostView);
		alertDialogBuilder.setTitle("Edit Desktop");
		
		alertDialogBuilder.setCancelable(true);
		
		final EditText ipedittext = (EditText) addhostView.findViewById(R.id.newipeditText);
		final EditText portedittext = (EditText) addhostView.findViewById(R.id.newporteditText);
		final CheckBox wifilockcheckbox = (CheckBox) addhostView.findViewById(R.id.wifilockcheckBox);
		final String currentwifi;
		ipedittext.setText(ip);
		portedittext.setText(""+port);
		
		if (wifi.equals("")) {
			wifilockcheckbox.setChecked(false);
			currentwifi = getWifiSSID();
			wifilockcheckbox.setText("only on "+currentwifi);
		}
		else {
			wifilockcheckbox.setChecked(true);
			currentwifi = wifi;
			wifilockcheckbox.setText("only on "+currentwifi);
		}		
		
		alertDialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				String ip = ipedittext.getText().toString();
				int port = Integer.parseInt(portedittext.getText().toString());	
				
	        	if (wifilockcheckbox.isChecked()) {
					// update DB
					dbhelper.updateHost(id, ip, port, currentwifi);	
	        	}
	        	else {
					// update DB
					dbhelper.updateHost(id, ip, port, "");	
	        	}    
				
	        	loadHostlist();
				
				dialog.cancel();					
			}
		});
		
		alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				dialog.cancel();				
			}
		});
		AlertDialog alertDialog = alertDialogBuilder.create();
		alertDialog.show();	
	}
		
	// get current SSID of connected Wifi
    private String getWifiSSID() {
    	String ssid = "";
    	WifiManager wifiManager = (WifiManager) getApplicationContext()
            		.getSystemService(Context.WIFI_SERVICE);
        try {
            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
            ssid = wifiInfo.getSSID();
        } catch (Exception a) { }
        
        if (ssid == null) {ssid = "";}
        return ssid;    	
    }
    
    public class DiscoveredHostsAdapter extends ArrayAdapter<DesktopHost> {

		public DiscoveredHostsAdapter(Context context, 
				ArrayList<DesktopHost> objects) {
			super(context,R.layout.host_list_row_share,objects);
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			DesktopHost desktop = getItem(position);
			
	        if (convertView == null) {
	           convertView = LayoutInflater.from(getContext()).inflate(R.layout.host_list_row_share, parent, false);
	        }
	        // Lookup view for data population
	        TextView tvName = (TextView) convertView.findViewById(R.id.hostnametextView);
	        TextView tvip = (TextView) convertView.findViewById(R.id.hostiptextView);
	        TextView tvid = (TextView) convertView.findViewById(R.id.hostidtextView);
	        TextView tvwifi = (TextView) convertView.findViewById(R.id.wifitextView);
	        // Populate the data into the template view using the data object
	        tvName.setText(desktop.name);
	        tvip.setText(desktop.ip);
	        tvid.setText(""+desktop.uuid);
	        tvwifi.setText("");
			
			return convertView;
		}    	
    }
    
    private class DiscoveryTask extends AsyncTask<Void, Void, Void> {
		private DatagramSocket serverSocketUDP;
		private boolean isStopped = false;
		private DiscoveredHostsAdapter dha;

		public DiscoveryTask(DiscoveredHostsAdapter dha) {
			this.dha = dha;
		}

		@Override
		protected Void doInBackground(Void... params) {
			Log.d("Discovery: ", "start UDP Server");	
			String data = "0::deskcon";
			try {		
				// Send "Ask for Hosts" Broadcast
				serverSocketUDP = new DatagramSocket(5108); 
				serverSocketUDP.setBroadcast(true);
				
				InetAddress local = getBroadcast();			
				
				// has a broadcast address been found
				if (local == null) { 
					Log.d("Discovery: ", "no Broadcast Address found");
					return null; 
				}
				
				DatagramPacket packet = new DatagramPacket(data.getBytes(), data.length(), local, 5108);
				serverSocketUDP.send(packet);
				
			} catch (Exception e) {
				e.printStackTrace();
				Log.d("Discovery: ", "could not start");	
				return null;
			} 
			
			WifiManager wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
			MulticastLock lock = wifi.createMulticastLock("net.screenfreeze.deskcon");
			lock.acquire();
			
			// Receive responses from desktop hosts
			while (!isStopped) {
				byte[] receiveData = new byte[128];
				DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
				
				try {
					serverSocketUDP.receive(receivePacket);
				} catch (Exception e) {}
				
                final InetAddress address = receivePacket.getAddress();
               
                String msg = new String(receiveData, 0, receivePacket.getLength());
				
				// Check for valid msg
				boolean isvalismsg = msg.split("::").length == 3;
				
				// Process msg if it is not our own broadcast msg
				if (!msg.equals(data) && !isStopped && isvalismsg) {
					
					final DesktopHost desktop = handleReceiveUdp(msg, address);               
	                Log.d("udp from: ", "msg "+msg+"  "+address);
	                
	                runOnUiThread(new Runnable() {
						
						@Override
						public void run() {
							dha.add(desktop);
						}
					});   
				}    
			}
			lock.release();
			
			return null;
		}
		
		private DesktopHost handleReceiveUdp(String msg, InetAddress address) {
			String[] split = msg.split("::");
			long uuid = Long.parseLong(split[0]);
			String hostname = split[1];
			int port = Integer.parseInt(split[2]);
			
			return new DesktopHost(hostname, ""+address.getHostAddress(), port, uuid);
		}
		
		// force server stop
		private void stopServer() {
			isStopped = true;
			try {
				serverSocketUDP.close();
			}
			catch (Exception e) {}
		}
    	
    }

    public class HostsAdapter extends CursorAdapter {

		@SuppressWarnings("deprecation")
		public HostsAdapter(Context context, Cursor c) {
			super(context, c);
		}

		@Override
		public void bindView(View view, final Context context, Cursor cursor) {
			TextView idtv = (TextView) view.findViewById(R.id.hostidtextView);
			TextView nametv = (TextView) view.findViewById(R.id.hostnametextView);
			TextView iptv = (TextView) view.findViewById(R.id.hostiptextView);
			TextView wifitv = (TextView) view.findViewById(R.id.wifitextView);
			ImageView deleteiv = (ImageView) view.findViewById(R.id.deletehostimageView);
			final long id = cursor.getLong(0);
			
			nametv.setText(cursor.getString(1));
			idtv.setText(String.valueOf(cursor.getLong(4)));
			iptv.setText(cursor.getString(2));
			wifitv.setText(cursor.getString(6));
					
			deleteiv.setOnClickListener(new OnClickListener() {
				
				@Override
				public void onClick(View v) {
					showRemoveHostDialog(context, id);
				}
			});			
		}
		
		// remove Host Dialog
		private void showRemoveHostDialog(Context context, final long id) {
			AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
			alertDialogBuilder.setTitle("Remove Desktop");
			alertDialogBuilder.setMessage("You want to remove\n"+id+" ?");			
			alertDialogBuilder.setCancelable(true);
			alertDialogBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					dbhelper.removeHost(id);
					authenticationmanager.deleteCertificate(id);
					loadHostlist();			
				}
			});
			alertDialogBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					dialog.cancel();				
				}
			});
			
			AlertDialog alertDialog = alertDialogBuilder.create();
			alertDialog.show();	
		}

		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
		    LayoutInflater inflater = LayoutInflater.from(context);

		    View v = inflater.inflate(R.layout.host_list_row, parent, false);
		    
			return v;
		}
    	
    }    
    
    public interface OnPairListener{
        void onPairingCompleted();
    }
    
    public class OnPairCallback implements OnPairListener{
        @Override
        public void onPairingCompleted() {
            loadHostlist();
        }
    }
    
    public class DesktopHost {
        public String name;
        public String ip;
        public int port;
        public long uuid;

        public DesktopHost(String name, String ip, int port, long uuid) {
           this.name = name;
           this.ip = ip;
           this.port = port;
           this.uuid = uuid;
        }    	
    }
    
    public static InetAddress getBroadcast(){
    InetAddress found_bcast_address=null;
     System.setProperty("java.net.preferIPv4Stack", "true"); 
        try
        {
          Enumeration<NetworkInterface> niEnum = NetworkInterface.getNetworkInterfaces();
          while (niEnum.hasMoreElements())
          {
            NetworkInterface ni = niEnum.nextElement();
            if(!ni.isLoopback()){
                for (InterfaceAddress interfaceAddress : ni.getInterfaceAddresses())
                {
                  found_bcast_address = interfaceAddress.getBroadcast();               
                }
            }
          }
        }
        catch (SocketException e)
        {
          e.printStackTrace();
        }

        return found_bcast_address;
    }
    
    public InetAddress getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        return inetAddress;
                    }
                }
            }
        } catch (SocketException ex) {}
        return null;
    }
}