package jp.agentec.abook.abv.cl.environment;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

import org.apache.http.conn.util.InetAddressUtils;

import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;

/**
 * Androidのネットワーク環境に関する機能を提供します。
 * 
 * 注意：初回のgetInstance()時に、必ずsetConnectivityManager(),setWifiManager()を呼び出すこと
 * 
 * @author Taejin Hong
 * @version 1.0.1
 */
public final class NetworkAdapter implements jp.agentec.abook.abv.bl.common.nw.NetworkAdapter {
	private static final String TAG = "NetworkAdapter";
	private static NetworkAdapter instance;
	private ConnectivityManager cm;
	private WifiManager wm;
	
	public static NetworkAdapter getInstance() {
		if (instance == null) {
			synchronized (NetworkAdapter.class) {
				if (instance == null) {
					instance = new NetworkAdapter();
				}
			}
		}
		return instance;
	}

	public void setConnectivityManager(ConnectivityManager cm) {
		this.cm = cm;
	}

	public void setWifiManager(WifiManager wm) {
		this.wm = wm;
	}

	/**
	 * Android端末がネットワークにつながっているかを確認します。
	 * @return ネットワークにつながっているとtrueを返します。ただし、これが、インターネットなどに接続できることを保証するわけではありません。また、Bluetooth接続はここで検出しません。
	 * @since 1.0.0
	 */
	@Override
    public boolean isNetworkConnected() {
		boolean connected = false;
		
		NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
		
		if (activeNetworkInfo != null) {
			Logger.d("Network State Changed", String.format("NetworkType:%s, State:%s", activeNetworkInfo.getTypeName(), activeNetworkInfo.getDetailedState().name()));
			if (activeNetworkInfo.getDetailedState().equals(DetailedState.CONNECTED)) {
				connected = true;
			}
        }
		return connected;
	}
	
	/**
	 * Android端末がWiFiにつながっているかを確認します。
	 * @return WiFiにつながっているとtrueを返します。
	 * @since 1.0.1
	 */
	@Override
    public boolean isWifiConnected() {
		boolean connected = false;
		
		NetworkInfo networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
		
		if (networkInfo != null) {
			connected = networkInfo.isConnected();
		}
		
		return connected;
	}
	
	/**
	 * Android端末のWi-FiのMACアドレスを取得します。
	 * @return Wi-FiのMACアドレス
	 * @since 1.0.0
	 */
	@Override
    public String getWifiAdapterMacAddress(Object context) {
		if (context instanceof Context) {
			final String key = "macaddress";
	        SharedPreferences preferences = ((Context)context).getSharedPreferences(this.getClass().getSimpleName(), Context.MODE_PRIVATE);
	        String macaddress = preferences.getString(key, null);
	        if (macaddress == null) {
				// Andoid6以降は取得しない
				if (android.os.Build.VERSION.SDK_INT < 23) {
					WifiInfo info = this.getWifiInfo();
					macaddress = info.getMacAddress();
					Editor editor = preferences.edit();
					editor.putString(key, macaddress);
					editor.commit();
					Logger.d("macaddress is saved Preferences!");
				}
			}
	        Logger.d("macaddress:" + key);
	        return macaddress;
		}
        return null;
	}
	
	private WifiInfo getWifiInfo() {
		return wm.getConnectionInfo();
	}

	@Override
	public int getActiveNetworkType() throws NetworkDisconnectedException {
		NetworkInfo nInfo = cm.getActiveNetworkInfo();
		int type;

		if (nInfo == null) {
			throw new NetworkDisconnectedException();
		}

		if (!nInfo.isConnected()) {
			throw new NetworkDisconnectedException();
		}

		type = nInfo.getType();

		return type;
	}
	
	/**
     *  端末のIPを取得 
    */
    public String 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() && InetAddressUtils.isIPv4Address(inetAddress.getHostAddress())) {
                        return inetAddress.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            Logger.e(TAG, "getLocalIpAddress failed", e);
        }
        return null;
    }

}
