/**
 * 
 */
package com.beem.project.beem;

import java.util.LinkedList;
import java.util.List;

import android.app.Activity;
import android.app.Application;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;

import com.beem.project.beem.service.aidl.IBeemConnectionListener;
import com.beem.project.beem.service.aidl.IXmppConnection;
import com.beem.project.beem.service.aidl.IXmppFacade;
import com.beem.project.beem.utils.Status;

/**
 * The Beem application. This class has some methods utiliy needs by the activities.
 * @author darisk
 */
public class BeemApplication extends Application {

    private static BeemApplication mBeemApp;
    private Activity mActivity;
    private static final Intent SERVICE_INTENT = new Intent();
    public static final String TAG = "BeemApplication";
    private IXmppFacade mFacade;
    private Context mApplicationContext;
    private List<Message> mQueue = new LinkedList<Message>();
    private boolean mIsConnected;
    private IXmppConnection mConnection;
    private ProgressDialog mProgressDialog;
    private ConnectionListener mConnectionListener = new ConnectionListener();

    static {
	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
    }

    private ServiceConnection mServConn = new ServiceConnection() {

	@Override
	public void onServiceDisconnected(ComponentName name) {
	    mFacade = null;
	    mIsConnected = false;
	}

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
	    mIsConnected = true;
	    mFacade = IXmppFacade.Stub.asInterface(service);
	    try {
		mConnection = mFacade.createConnection();
		if (!mConnection.isAuthentificated()) {
		    mConnection.addConnectionListener(mConnectionListener);
		    mApplicationContext.startService(BeemApplication.SERVICE_INTENT);
		}else {
		    mProgressDialog.dismiss();
		    synchronized (mQueue) {
			for (Message msg : mQueue) {
			    msg.sendToTarget();
			}
			mQueue.clear();
		    }
		}
	    } catch (RemoteException e) {
		Log.e(TAG, "service connection exception", e);
	    }
	}
    };

    /**
     * Constructor.
     */
    public BeemApplication() {
	mIsConnected = false;
    }

    /**
     * Get the Beem application for an activity.
     * @param activity the activity which want the Beem application
     * @return the Beem application
     */
    public static BeemApplication getApplication(Activity activity) {
	if (mBeemApp == null) {
	    mBeemApp = new BeemApplication();
	}
	mBeemApp.mActivity = activity;
	mBeemApp.mProgressDialog = new ProgressDialog(activity);
	mBeemApp.mProgressDialog.setTitle("Beem");
	mBeemApp.mProgressDialog.setIcon(R.drawable.signal);
	mBeemApp.mProgressDialog.setMessage("Connecting...");
	mBeemApp.mApplicationContext = activity.getApplication();
	activity.getResources();
	mBeemApp.onCreate();
	return mBeemApp;
    }

    /**
     * Start the beem service.
     */
    public synchronized void startBeemService() {
	if (!mIsConnected) {
	    mProgressDialog.setMessage("Connecting...");
	    mProgressDialog.show();
	    // the connection will be made on service connect
	    mApplicationContext.bindService(BeemApplication.SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
	}
    }

    /**
     * Stop the Beem service.
     */
    public synchronized void stopBeemService() {
	if (mIsConnected) {
	    Intent intent = new Intent();
	    intent.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
	    mApplicationContext.unbindService(mServConn);
	    mApplicationContext.stopService(intent);
	    mIsConnected = false;
	}
    }

    public synchronized void unbindBeemService() {
	if (mIsConnected) {
	    mApplicationContext.unbindService(mServConn);
	    mIsConnected = false;
	}
     }

    /**
     * Get the facade to use to access the Beem service.
     * @return the facade or null if the application is not connected to the beem service.
     */
    public IXmppFacade getXmppFacade() {
	return mFacade;
    }

    /**
     * Add a methode to execute when the application is connected to the server.
     * @param target the handler which will execute the callback
     * @param callback the callback to execute
     */
    public void callWhenConnectedToServer(Handler target, Runnable callback) {
	Message msg = Message.obtain(target, callback);
	if (mIsConnected) {
	    msg.sendToTarget();
	} else {
	    startBeemService();
	    synchronized (mQueue) {
		mQueue.add(msg);
	    }
	}
    }

    /**
     * Tell if we are connected with the Beem Service.
     * @return true if connected false otherwise
     */
    public boolean isConnected() {
	return mIsConnected;
    }

    private class ConnectionRunnable implements Runnable {
	private String mErrorMsg;

	public ConnectionRunnable(String string) {
	    this.mErrorMsg = string;
	}

	@Override
	public void run() {
	    mBeemApp.mProgressDialog.setMessage(mErrorMsg);
	}

	/**
	 * @param mErrorMsg the mErrorMsg to set
	 */
	public void setMErrorMsg(String mErrorMsg) {
	    this.mErrorMsg = mErrorMsg;
	}

	/**
	 * @return the mErrorMsg
	 */
	public String getMErrorMsg() {
	    return mErrorMsg;
	}

    }

    /**
     * Connection listener use to hide the progress dialog.
     * @author darisk
     */
    private class ConnectionListener extends IBeemConnectionListener.Stub {

	/**
	 * Constructor.
	 */
	public ConnectionListener() {
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void connectionClosed() throws RemoteException {
	    Log.e(TAG, "Connection Close");
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void connectionClosedOnError() throws RemoteException {
	    mBeemApp.mProgressDialog.setMessage("Connection closed on error");
	    Log.e(TAG, "ConnectionClosedOnError");
	    // TODO afficher une notification et reafficher le progress dialog
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onConnect() throws RemoteException {
	    // TODO Auto-generated method stub
	    mProgressDialog.dismiss();
	    // TODO recuperer les informations de status dans les preferences
	    mFacade.changeStatus(Status.CONTACT_STATUS_AVAILABLE, null);
	    synchronized (mQueue) {
		for (Message msg : mQueue) {
		    msg.sendToTarget();
		}
		mQueue.clear();
	    }
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void reconnectingIn(int seconds) throws RemoteException {
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void reconnectionFailed() throws RemoteException {
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void reconnectionSuccessful() throws RemoteException {
	}

	@Override
	public void connectionFailed(String errorMsg) throws RemoteException {
	    Log.i(TAG, "Connection Failed");
	    ConnectionRunnable cRun = new ConnectionRunnable(errorMsg);
	    mBeemApp.mActivity.runOnUiThread(cRun);
	}

    }

}
