package org.thoughtcrime.securesms.mms;

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.os.PowerManager;
import android.util.Log;

import org.thoughtcrime.securesms.util.Util;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MmsRadio {

  private static final String TAG = MmsRadio.class.getSimpleName();

  private static MmsRadio instance;

  public static synchronized MmsRadio getInstance(Context context) {
    if (instance == null)
      instance = new MmsRadio(context.getApplicationContext());

    return instance;
  }

  ///

  private static final String FEATURE_ENABLE_MMS = "enableMMS";
  private static final int APN_ALREADY_ACTIVE    = 0;
  public  static final int TYPE_MOBILE_MMS       = 2;

  private final Context context;

  private ConnectivityManager   connectivityManager;
  private ConnectivityListener  connectivityListener;
  private PowerManager.WakeLock wakeLock;
  private int connectedCounter = 0;

  private MmsRadio(Context context) {
    PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    this.context             = context;
    this.connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    this.wakeLock            = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MMS Connection");
    this.wakeLock.setReferenceCounted(true);
  }

  public synchronized void disconnect() {
    Log.w("MmsRadio", "MMS Radio Disconnect Called...");
    wakeLock.release();
    connectedCounter--;

    Log.w("MmsRadio", "Reference count: " + connectedCounter);

    if (connectedCounter == 0) {
      Log.w("MmsRadio", "Turning off MMS radio...");
      try {
        final Method stopUsingNetworkFeatureMethod = connectivityManager.getClass().getMethod("stopUsingNetworkFeature", Integer.TYPE, String.class);
        stopUsingNetworkFeatureMethod.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
      } catch (NoSuchMethodException nsme) {
        Log.w(TAG, nsme);
      } catch (IllegalAccessException iae) {
        Log.w(TAG, iae);
      } catch (InvocationTargetException ite) {
        Log.w(TAG, ite);
      }
      
      if (connectivityListener != null) {
        Log.w("MmsRadio", "Unregistering receiver...");
        context.unregisterReceiver(connectivityListener);
        connectivityListener = null;
      }
    }
  }

  public synchronized void connect() throws MmsRadioException {
    int status;

    try {
      final Method startUsingNetworkFeatureMethod = connectivityManager.getClass().getMethod("startUsingNetworkFeature", Integer.TYPE, String.class);
      status = (int)startUsingNetworkFeatureMethod.invoke(connectivityManager, ConnectivityManager.TYPE_MOBILE, FEATURE_ENABLE_MMS);
    } catch (NoSuchMethodException nsme) {
      throw new MmsRadioException(nsme);
    } catch (IllegalAccessException iae) {
      throw new MmsRadioException(iae);
    } catch (InvocationTargetException ite) {
      throw new MmsRadioException(ite);
    }

    Log.w("MmsRadio", "startUsingNetworkFeature status: " + status);

    if (status == APN_ALREADY_ACTIVE) {
      wakeLock.acquire();
      connectedCounter++;
      return;
    } else {
      wakeLock.acquire();
      connectedCounter++;

      if (connectivityListener == null) {
        IntentFilter filter  = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
        connectivityListener = new ConnectivityListener();
        context.registerReceiver(connectivityListener, filter);
      }

      Util.wait(this, 30000);

      if (!isConnected()) {
        Log.w("MmsRadio", "Got back from connectivity wait, and not connected...");
        disconnect();
        throw new MmsRadioException("Unable to successfully enable MMS radio.");
      }
    }
  }

  private boolean isConnected() {
    NetworkInfo info = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);

    Log.w("MmsRadio", "Connected: " + info);

    if ((info == null) || (info.getType() != TYPE_MOBILE_MMS) || !info.isConnected())
      return false;

    return true;
  }

  private boolean isConnectivityPossible() {
    NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);

    return networkInfo != null  && networkInfo.isAvailable();
  }

  private boolean isConnectivityFailure() {
    NetworkInfo networkInfo = connectivityManager.getNetworkInfo(TYPE_MOBILE_MMS);

    return networkInfo == null || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED;
  }

  private synchronized void issueConnectivityChange() {
    if (isConnected()) {
      Log.w("MmsRadio", "Notifying connected...");
      notifyAll();
      return;
    }

    if (!isConnected() && (isConnectivityFailure() || !isConnectivityPossible())) {
      Log.w("MmsRadio", "Notifying not connected...");
      notifyAll();
      return;
    }
  }

  private class ConnectivityListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      Log.w("MmsRadio", "Got connectivity change...");
      issueConnectivityChange();
    }
  }


}
