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

import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Point;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
import android.os.StatFs;
import android.provider.Settings.Secure;
import android.util.Base64;
import android.view.Display;
import android.view.WindowManager;

import java.security.Key;
import java.util.List;
import java.util.UUID;

import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.Constant.DeviceIdType;
import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.ABVExceptionCode;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.SecurityUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.UserPrefKey;
import jp.agentec.adf.util.StringUtil;

/**
 * Android端末のデバイス情報にアクセスします。
 * @author Taejin Hong
 * @version　1.0.0 
 */
public class DeviceInfo {
	private static final String TAG = "DeviceInfo";

	public static void printNativeHeapStatus() {
		Logger.d(TAG, "Heap size : %d, %d free, %d allocated.", Debug.getNativeHeapSize(), Debug.getNativeHeapFreeSize(), Debug.getNativeHeapAllocatedSize());
	}
	
	public static String getScreenSizeForAcmsParam(Context context) {
		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		Display display = wm.getDefaultDisplay();
		Point point = new Point();
		display.getSize(point);
		int disWidth = point.x;
		int disHeight = point.y;
		if (Build.VERSION.SDK_INT > 12) { // Versionが3.1以降の場合はメニューコントロールパネルの高さが引かれているため
			disHeight += 48;
		}
		
		int longPart;
        int shortPart;
        if (disWidth > disHeight) {
			// Landscape
			longPart = disWidth;
			shortPart = disHeight;
		} else {
			// Portrait
			longPart = disHeight;
			shortPart = disWidth;
		}
		return String.format("%dx%d", longPart, shortPart);
	}

	/**
	 * バッググラウンドか実行中か収得。<br>
	 * @throws ClassNotFoundException 引数のcontextが {@link android.content.Context} 又は、その継承クラスではありません。
	 * @return 実行中の場合TRUE。
	 * @since 1.0.0
	 */
	public static boolean isForegrdound(Context context) {
	    ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
		//noinspection deprecation(API21から非推奨になった。無視)
	    List<RunningTaskInfo> list = am.getRunningTasks(1);
		if (list != null && list.size() > 0) {
			ComponentName cn = list.get(0).topActivity;
			if (cn != null) {
				String name = cn.getPackageName();
				if (name != null) {
					return name.contains(context.getPackageName());
				} else {
					Logger.d(TAG, "packageName is null");
				}
			} else {
				Logger.d(TAG, "ComponentName is null");
			}
		} else {
			Logger.d(TAG, "getRunningTasks is empty");
		}
		return false;
	}

	private static boolean isEmulator() {
		return (android.os.Build.PRODUCT.toLowerCase().equals("sdk"));
	}

	public static int getDeviceIdType(Context context, boolean isDebug) throws ABVException {
		String deviceId = getWiFiMacAddress(context, isDebug);
		int type = DeviceIdType.MAC_ADDRESS;
		if (deviceId == null && android.os.Build.VERSION.SDK_INT >= 23) {
			String androidId = getAndroidId(context);
			if (StringUtil.isNullOrEmpty(androidId)) {
				type = DeviceIdType.UUID;
			} else {
				type = DeviceIdType.ANDROID_ID;
			}
		}
		return type;
	}

	public static String getDeviceId(Context context, boolean isDebug) throws ABVException {
		String deviceId = getWiFiMacAddress(context, isDebug);
		if (deviceId == null && android.os.Build.VERSION.SDK_INT >= 23) {
			deviceId = getAndroidId(context);
			if (StringUtil.isNullOrEmpty(deviceId)) {
				deviceId = getUUId(context);
			}
		}
		return deviceId;
	}

	/**
	 * 端末のWiFiアダプターのMACアドレスを取得します。
	 * @param isDebug trueを指定すると、アプリをエミュレーターで実行している場合のみ、Wi-Fiが使えなくてもデバイスIDを返します。
	 * @return 端末のWiFiアダプターのMACアドレスを返します。エミュレーターの場合、仮のMACアドレス（固定値）を返します。
	 * @throws NetworkDisconnectedException 
	 * @since 1.1.0
	 */
	private static String getWiFiMacAddress(Context context, boolean isDebug) throws ABVException {
		String mac = getWiFiMacAddressNoEncrypt(context, isDebug);
		String result = null;
		if(mac != null){
			Key key = SecurityUtil.generateKey(ABVEnvironment.MacAddressAESKey);
			byte[] encrypted = SecurityUtil.encryptECB(mac, key);
			result = new String(Base64.encodeToString(encrypted, Base64.NO_WRAP));
			result = StringUtil.urlEncode(result, "UTF-8");
		}

		//Logger.d("MAC original : " + mac + ", encrypted : " + result + ", key : " + ABVEnvironment.MacAddressAESKey);
		return result;
	}

	private static String getAndroidId(Context context){
		SharedPreferences preferences = context.getSharedPreferences(NetworkAdapter.class.getSimpleName(), Context.MODE_PRIVATE);
		final String keyAndroidId = "androidid";
		String androidId = preferences.getString(keyAndroidId, null);
		if (androidId == null) {
			androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID) + "." + keyAndroidId;
			Editor editor = preferences.edit();
			editor.putString(keyAndroidId, androidId);
			editor.commit();
		}
		return androidId;
	}

	private static String getUUId(Context context){
		SharedPreferences preferences = context.getSharedPreferences(NetworkAdapter.class.getSimpleName(), Context.MODE_PRIVATE);
		final String keyUuid = "uuid";
		String uuid = preferences.getString(keyUuid, null);
		if (StringUtil.isNullOrEmpty(uuid)) {
			uuid = UUID.randomUUID().toString() + "." + keyUuid;
			Editor editor = preferences.edit();
			editor.putString(keyUuid, uuid);
			editor.commit();
			Logger.d("uuid is saved Preferences!");
		}
		return uuid;
	}

	public static String getDeviceUUID(Context context) {
		String uuid = PreferenceUtil.getUserPref(context, UserPrefKey.DEVICE_UUID, "");
		if (uuid.isEmpty()) {
			uuid = UUID.randomUUID().toString();
			PreferenceUtil.putUserPref(context, UserPrefKey.DEVICE_UUID, uuid);
		}
		return uuid;
	}

	public static String getEncryptKey(Context context, boolean isDebug) throws ABVException {
		return SecurityUtil.getMd5Hash(getDeviceId(context,isDebug));
	}

	/**
	 * Macアドレスをハッシュ化した値をTokenとして生成して返します。
	 * @param isDebug trueを指定すると、エミュレータで実行している場合のみ、固定値を返します。
	 * @throws NetworkDisconnectedException 
	 * @since 1.1.0
	 */
	public static String getEncryptKeyByMacAddress(Context context, boolean isDebug) throws ABVException {
		String mac = getWiFiMacAddressNoEncrypt(context, isDebug);
		return SecurityUtil.getMd5Hash(mac);
	}
	
	public static String getEncryptKeyByUUID(Context context) {
		String uuid = getDeviceUUID(context);
		return SecurityUtil.getMd5Hash(uuid);
	}
	
	private static String getWiFiMacAddressNoEncrypt(Context context, boolean isDebug) throws ABVException {
		String mac;
		
		if (isDebug && isEmulator()) {
			mac = "on-emulator";
		} else {
			NetworkAdapter na = NetworkAdapter.getInstance();
			na.setConnectivityManager((ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE));
			na.setWifiManager((WifiManager)context.getSystemService(Context.WIFI_SERVICE));
			mac = na.getWifiAdapterMacAddress(context);
		}
		
		if (StringUtil.isNullOrWhiteSpace(mac) && android.os.Build.VERSION.SDK_INT < 23) {
			throw new ABVException(ABVExceptionCode.C_E_SYSTEM_0004, "Wi-Fi network is unavailable. Please check your Wi-Fi status.");
		}
		
		return mac;
	}

	/**
	 * 外部ストレージの空き容量を取得します。
	 * @return 外部ストレージの空き容量を返します。
	 * @since 1.1.0
	 */
	public static long getFreeExternalStorageSpace() {
		StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath());
		//noinspection deprecation(API18から非推奨になった。無視)
		return (long)stat.getBlockSize() * stat.getBlockCount();
	}

	/**
	 * 内部ストレージの空き容量を取得します。
	 * @return 内部ストレージの空き容量を返します。
	 * @since 1.1.0
	 */
	public static long getFreeStorageSpace() {
		StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath());
		//noinspection deprecation(API18から非推奨になった。無視)
		return (long)stat.getBlockSize() * stat.getAvailableBlocks();
	}

	/**
	 * 外部ストレージの容量を取得します。
	 * @return 外部ストレージの容量を返します。
	 * @since 1.1.0
	 */
	public static long getTotalExternalStorageSpace() {
		StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
		//noinspection deprecation(API18から非推奨になった。無視)
		return (long)stat.getBlockSize() * stat.getBlockCount();
	}

	/**
	 * 内部ストレージの容量を取得します。
	 * @return 内部ストレージの容量を返します。
	 * @since 1.1.0
	 */
	public static long getTotalStorageSpace() {
		StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
		//noinspection deprecation(API18から非推奨になった。無視)
		return (long)stat.getBlockSize() * stat.getAvailableBlocks();
	}
}
