/*
 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
 * 
 * Copyright (c) 2005-2010, Nitobi Software Inc.
 * Copyright (c) 2010-2011, IBM Corporation
 */
package com.phonegap;

import org.json.JSONArray;

import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;

public class NetworkManager extends Plugin {
	
    public static int NOT_REACHABLE = 0;
	public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
	public static int REACHABLE_VIA_WIFI_NETWORK = 2;

	public static final String WIFI = "wifi";
	public static final String WIMAX = "wimax";
	// mobile
	public static final String MOBILE = "mobile";
	// 2G network types
	public static final String GSM = "gsm";
	public static final String GPRS = "gprs";
	public static final String EDGE = "edge";
	// 3G network types
	public static final String CDMA = "cdma";
	public static final String UMTS = "umts";
	// 4G network types
	public static final String LTE = "lte";
	public static final String UMB = "umb";
	// return types
	public static final String TYPE_UNKNOWN = "unknown";
	public static final String TYPE_ETHERNET = "ethernet";
	public static final String TYPE_WIFI = "wifi";
	public static final String TYPE_2G = "2g";
	public static final String TYPE_3G = "3g";
	public static final String TYPE_4G = "4g";
	public static final String TYPE_NONE = "none";
	
	private static final String LOG_TAG = "NetworkManager";

	private String connectionCallbackId;

	ConnectivityManager sockMan;
	BroadcastReceiver receiver;
	
	/**
	 * Constructor.
	 */
	public NetworkManager()	{
		this.receiver = null;
	}

	/**
	 * Sets the context of the Command. This can then be used to do things like
	 * get file paths associated with the Activity.
	 * 
	 * @param ctx The context of the main Activity.
	 */
	public void setContext(PhonegapActivity ctx) {
		super.setContext(ctx);
		this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);		
		this.connectionCallbackId = null;
		
		// We need to listen to connectivity events to update navigator.connection
		IntentFilter intentFilter = new IntentFilter() ;
		intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
		if (this.receiver == null) {
			this.receiver = new BroadcastReceiver() {
				@Override
				public void onReceive(Context context, Intent intent) {				
					updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));				
				}
			};
			ctx.registerReceiver(this.receiver, intentFilter);
		}

	}
	
	/**
	 * Executes the request and returns PluginResult.
	 * 
	 * @param action 		The action to execute.
	 * @param args 			JSONArry of arguments for the plugin.
	 * @param callbackId	The callback id used when calling back into JavaScript.
	 * @return 				A PluginResult object with a status and message.
	 */
	public PluginResult execute(String action, JSONArray args, String callbackId) {
		PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
		String result = "Unsupported Operation: " + action;	
		
		if (action.equals("getConnectionInfo")) {
			this.connectionCallbackId = callbackId;
			NetworkInfo info = sockMan.getActiveNetworkInfo();
			PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
			pluginResult.setKeepCallback(true);
			return pluginResult;
		}
		
		return new PluginResult(status, result);
	}

	/**
	 * Identifies if action to be executed returns a value and should be run synchronously.
	 * 
	 * @param action	The action to execute
	 * @return			T=returns value
	 */
	public boolean isSynch(String action) {
		// All methods take a while, so always use async
		return false;
	}
	
	/**
	 * Stop network receiver.
	 */
	public void onDestroy() {
		if (this.receiver != null) {
			try {
				this.ctx.unregisterReceiver(this.receiver);
			} catch (Exception e) {
				Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
			}
		}
	}

    //--------------------------------------------------------------------------
    // LOCAL METHODS
    //--------------------------------------------------------------------------


	/**
	 * Updates the JavaScript side whenever the connection changes
	 * 
	 * @param info the current active network info
	 * @return
	 */
	private void updateConnectionInfo(NetworkInfo info) {	 
        // send update to javascript "navigator.network.connection"
        sendUpdate(this.getConnectionInfo(info));
	}

	/** 
	 * Get the latest network connection information
	 * 
	 * @param info the current active network info
	 * @return a JSONObject that represents the network info
	 */
	private String getConnectionInfo(NetworkInfo info) {
	    String type = TYPE_NONE;
		if (info != null) {
			// If we are not connected to any network set type to none
			if (!info.isConnected()) {
				type = TYPE_NONE;
			}			
			else {
				type = getType(info);
			}
		}
        return type;
	}
	
	/**
	 * Create a new plugin result and send it back to JavaScript
	 * 
	 * @param connection the network info to set as navigator.connection
	 */
	private void sendUpdate(String type) {
		PluginResult result = new PluginResult(PluginResult.Status.OK, type);
		result.setKeepCallback(true);
		this.success(result, this.connectionCallbackId);
	}
	
	/**
	 * Determine the type of connection
	 * 
	 * @param info the network info so we can determine connection type.
	 * @return the type of mobile network we are on
	 */
	private String getType(NetworkInfo info) {
		if (info != null) {
			String type = info.getTypeName(); 

            if (type.toLowerCase().equals(WIFI)) {
                return TYPE_WIFI;
            }
            else if (type.toLowerCase().equals(MOBILE)) {
				type = info.getSubtypeName();
				if (type.toLowerCase().equals(GSM) || 
						type.toLowerCase().equals(GPRS) ||
						type.toLowerCase().equals(EDGE)) {
					return TYPE_2G;
				}
				else if (type.toLowerCase().equals(CDMA) || 
						type.toLowerCase().equals(UMTS)) {
					return TYPE_3G;
				}
				else if (type.toLowerCase().equals(LTE) || 
						type.toLowerCase().equals(UMB)) {
					return TYPE_4G;
				}
			}
		} 
		else {
			return TYPE_NONE;
		}
		return TYPE_UNKNOWN;
	}
}
