package net.screenfreeze.deskcon;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

import org.json.JSONException;
import org.json.JSONObject;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.Cursor;
import android.media.AudioManager;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.StatFs;
import android.preference.PreferenceManager;
import android.provider.CallLog;
import android.util.Log;

public class StatusUpdateService extends Service {
	private static String UUID;
	private static String PNAME;
	private String[] HOSTS;
	private int[] PORTS;
	private DesktopHostsDBHelper dbhelper;
	private SharedPreferences sharedPrefs;
	
	@SuppressLint("ShowToast")
	@Override
	public void onCreate() {
		sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

		super.onCreate();
	}
	
	private void loadIdentifiers() {
        UUID = sharedPrefs.getString("uuid", "0000000011111111");
        String storedname = sharedPrefs.getString("device_name", "");
        if (storedname.equals("")) {
        	PNAME = android.os.Build.MODEL;
        }
        else {
        	PNAME = storedname;
        }        
	}
	
	private void loadHosts() {
		dbhelper = new DesktopHostsDBHelper(this);		
    	String current_wifi = getWifiSSID();
    	
    	Cursor cursor = dbhelper.getHostsOnWifiCursor(current_wifi);
    	int hostcount = cursor.getCount();			    	

    	HOSTS = new String[hostcount];
    	PORTS = new int[hostcount];
    	
        if (cursor.moveToFirst()) {
            do {    
            	HOSTS[cursor.getPosition()] = cursor.getString(2);
            	PORTS[cursor.getPosition()] = cursor.getInt(3);
            } while (cursor.moveToNext());
        }
        
        cursor.close();
        dbhelper.close();
	}
	
	@SuppressLint("NewApi")
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		loadIdentifiers();
		loadHosts();
		if ( HOSTS.length == 0 ) {
			stopSelf();
			return super.onStartCommand(intent, flags, startId);
		}
		
		Bundle extras = intent.getExtras();

		if (extras == null || !extras.containsKey("commandtype")) {
			sendStatusUpdate();
		}
		else {
			sendStatusUpdate();
//			SendDataClient dataclient = new SendDataClient();			
//			//exe in parallel
//			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
//			    dataclient.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, extras);
//			} else {
//			    dataclient.execute(extras);
//			}
			SendDataThread datathread = new SendDataThread(extras);
		    Thread dt = new Thread(datathread);
		    dt.start();
		}
				
		return super.onStartCommand(intent, flags, startId); 
	}
	
	@SuppressLint("NewApi")
	private void sendStatusUpdate() {
		String jsonstr = getCurrentStatus();
		Bundle data = new Bundle();
		data.putString("commandtype", "STATS");
		data.putString("message", jsonstr);
//		SendDataClient dataclient = new SendDataClient();
//		
//		//exe in parallel
//		if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
//		    dataclient.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, data);
//		} else {
//		    dataclient.execute(data);
//		}
		
		SendDataThread datathread = new SendDataThread(data);
	    Thread dt = new Thread(datathread);
	    dt.start();
	}

	// Building Protocol MSG
	private static String buildmsg(String type, String msgstr) {
        JSONObject jobject = new JSONObject();
        try {
			jobject.put("uuid", UUID);        
			jobject.put("devicename", PNAME);
	        jobject.put("type", type);
	        jobject.put("data", msgstr);
		} catch (JSONException e) {
			e.printStackTrace();
		}   
        
		return jobject.toString();
	}
		
	//get current Stats
	@SuppressWarnings("deprecation")
	@SuppressLint("NewApi")
	private String getCurrentStatus() {
		// get Permissions
		boolean allow_control = sharedPrefs.getBoolean("allow_control", false);
		boolean allow_compose_message = sharedPrefs.getBoolean("allow_compose_message", false);
		boolean send_vol = sharedPrefs.getBoolean("send_vol", false);
		boolean send_bat = sharedPrefs.getBoolean("send_bat", false);
		boolean send_messages = sharedPrefs.getBoolean("send_messages", false);
		boolean send_calls = sharedPrefs.getBoolean("send_calls", false);
		boolean send_storage = sharedPrefs.getBoolean("send_storage", false);
		
		//create json object to send
        JSONObject jobject = new JSONObject();
        
        PackageManager pm = getApplicationContext().getPackageManager();
        boolean has_telephone = pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
        	
		// get control permissions
    	if (allow_control) {
    		int control_port = Integer.parseInt(sharedPrefs.getString("control_port", "9096"));
    		try {
				jobject.put("controlport", control_port);
			} catch (JSONException e) {
				e.printStackTrace();
			}
    		
    		//get sms capability    		
    		boolean can_message = has_telephone;
    		
    		try {
				jobject.put("canmessage", can_message && allow_compose_message);
			} catch (JSONException e) {
				e.printStackTrace();
			}    		
		}
    	
    	// get used Storage
    	if (send_storage) {
    		long KILOBYTE = 1024;
    		
    		StatFs internalStatFs = new StatFs( Environment.getDataDirectory().getAbsolutePath() );
    		long internalTotal;
    		long internalFree;

    		StatFs externalStatFs = new StatFs( Environment.getExternalStorageDirectory().getAbsolutePath() );
    		long externalTotal;
    		long externalFree;

    		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
    		    internalTotal = ( internalStatFs.getBlockCountLong() * internalStatFs.getBlockSizeLong() ) / ( KILOBYTE * KILOBYTE );
    		    internalFree = ( internalStatFs.getAvailableBlocksLong() * internalStatFs.getBlockSizeLong() ) / ( KILOBYTE * KILOBYTE );
    		    externalTotal = ( externalStatFs.getBlockCountLong() * externalStatFs.getBlockSizeLong() ) / ( KILOBYTE * KILOBYTE );
    		    externalFree = ( externalStatFs.getAvailableBlocksLong() * externalStatFs.getBlockSizeLong() ) / ( KILOBYTE * KILOBYTE );
    		}
    		else {
    		    internalTotal = ( (long) internalStatFs.getBlockCount() * (long) internalStatFs.getBlockSize() ) / ( KILOBYTE * KILOBYTE );
    		    internalFree = ( (long) internalStatFs.getAvailableBlocks() * (long) internalStatFs.getBlockSize() ) / ( KILOBYTE * KILOBYTE );
    		    externalTotal = ( (long) externalStatFs.getBlockCount() * (long) externalStatFs.getBlockSize() ) / ( KILOBYTE * KILOBYTE );
    		    externalFree = ( (long) externalStatFs.getAvailableBlocks() * (long) externalStatFs.getBlockSize() ) / ( KILOBYTE * KILOBYTE );
    		}

    		long total = internalTotal + externalTotal;
    		long free = internalFree + externalFree;
    		long used = total - free;
    		
    		int storagePer = Math.round((used / (float)total) * 100);

    		try {
				jobject.put("storage", storagePer);
			} catch (JSONException e) {
				e.printStackTrace();
			}    		
    	}
    	
    	//get Volume
    	if (send_vol) {
	        AudioManager audioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
	        
	        int cvolume = audioManager.getStreamVolume(AudioManager.STREAM_RING);
	        int maxvolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
	        int volumePer = Math.round((cvolume / (float)maxvolume) * 100);
    		
    		try {
				jobject.put("volume", volumePer);
			} catch (JSONException e) {
				e.printStackTrace();
			}    		
    	}
    	
	    //get Battery
    	if (send_bat) {
    		IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
	        Intent batteryStatus = getApplicationContext().registerReceiver(null, ifilter);
	        // Are we charging / charged?
	        int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
	        
	        boolean batteryisCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
	                             status == BatteryManager.BATTERY_STATUS_FULL;
	        
	        int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
	        int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
	
	        int batteryPer = Math.round((level / (float)scale) * 100);
	        
    		try {
				jobject.put("battery", batteryPer);
				jobject.put("batterystate", batteryisCharging);
			} catch (JSONException e) {
				e.printStackTrace();
			}
		}  

        //get missed Calls
    	if (send_calls) {        
    		String[] projection = { CallLog.Calls.CACHED_NAME, CallLog.Calls.CACHED_NUMBER_LABEL, CallLog.Calls.TYPE };
	        String where = CallLog.Calls.TYPE+"="+CallLog.Calls.MISSED_TYPE+ " AND " + CallLog.Calls.NEW + "=1";          
	        Cursor c = this.getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, where, null, null);
	        c.moveToFirst();
	        int missedCalls = c.getCount();
	        c.close();
	        
    		try {
				jobject.put("missedcalls", missedCalls);
			} catch (JSONException e) {
				e.printStackTrace();
			}
    	}
        
        //get missed Messages
    	if (send_messages) {
	    	Uri sms_content = Uri.parse("content://sms/inbox");
	        Cursor cm = this.getContentResolver().query(sms_content, null,"read = 0", null, null);
	        cm.moveToFirst();
	        int missedMsgs = cm.getCount();
	        cm.close();
	        
	        try {
				jobject.put("missedmsgs", missedMsgs);
			} catch (JSONException e) {
				e.printStackTrace();
			}
    	}
    	
    	//get Wifi strength
    	int wifistrength = getWifiStrength();
        try {
			jobject.put("wifistrength", wifistrength);
		} catch (JSONException e) {
			e.printStackTrace();
		}    	
    	
		return jobject.toString();
	}
	
	public static boolean isTablet(Context context) {
	    return (context.getResources().getConfiguration().screenLayout
            & Configuration.SCREENLAYOUT_SIZE_MASK)
            >= Configuration.SCREENLAYOUT_SIZE_LARGE;
	}
	
	private class SendDataThread implements Runnable {
		private String type;
		private String message;
		private Bundle data;
		
		public SendDataThread(Bundle data) {
			type = data.getString("commandtype");
			message = data.getString("message");	
			this.data = data;
		}

		@SuppressLint("Wakelock")
		@Override
		public void run() {
			PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
			PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Status Update WL");
			wl.acquire();
			
			if (data.containsKey("host")) {
				// send only to one Host
				String HOST = data.getString("host");
				int PORT = data.getInt("port");
				
				try {
					sendData(HOST, PORT);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			else {
				//send to all Hosts
				for (int i=0; i<HOSTS.length; i++) {
					try {
						sendData(HOSTS[i], PORTS[i]);
					} catch (Exception e) {
						e.printStackTrace();
					}				
				}
			}
			
			wl.release();			
		}
		
		private void sendData(String host, int port) throws Exception {
			Socket sslsocket = null;
			try {
				// create SSl Connection
				sslsocket = Connection.createSSLSocket(getApplicationContext(), host, port);
			} catch (Exception e) {
				Log.d("Connection: ", "could not connect");
				return;
			}
		    
			// write data
			OutputStream out = sslsocket.getOutputStream();
			String data = buildmsg(type, message);
			out.write(data.getBytes());
			sslsocket.close();
	    }	
		
	}
	
	//old code
	private class SendDataClient extends AsyncTask<Bundle, Void, Void> {
		private String type;
		private String message;

		@SuppressLint("Wakelock")
		@Override
		protected Void doInBackground(Bundle... params) {
			Bundle data = params[0];
			type = data.getString("commandtype");
			message = data.getString("message");
			
			PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
			PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Status Update WL");
			wl.acquire();			
			
			if (data.containsKey("host")) {
				// send only to one Host
				String HOST = data.getString("host");
				int PORT = data.getInt("port");
				
				try {
					sendData(HOST, PORT);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			else {
				//send to all Hosts
				for (int i=0; i<HOSTS.length; i++) {
					try {
						sendData(HOSTS[i], PORTS[i]);
					} catch (Exception e) {
						e.printStackTrace();
					}				
				}
			}
			
			wl.release();
			
			stopSelf();
			
			return null;
		}		
		
		private void sendData(String host, int port) throws Exception {
			Socket sslsocket = null;
			try {
				host = InetAddress.getByName(host).getHostAddress();
				// create SSl Connection
				sslsocket = Connection.createSSLSocket(getApplicationContext(), host, port);
			} catch (Exception e) {
				Log.d("Connection: ", "could not connect");
				return;
			}
		    
			// write data
			OutputStream out = sslsocket.getOutputStream();
			String data = buildmsg(type, message);
			out.write(data.getBytes());
			sslsocket.close();
	    }		
	}
	
	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}
	
    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;    	
    }
    
    private int getWifiStrength() {
        try {
      	   WifiManager wifiManager = (WifiManager) getApplicationContext()
           		.getSystemService(Context.WIFI_SERVICE);
            int rssi = wifiManager.getConnectionInfo().getRssi();
            int level = WifiManager.calculateSignalLevel(rssi, 10);
            int percentage = (int) ((level/10.0)*100);
            return percentage;
        }
        catch (Exception e) {
           return -1;
        }
	}        
}