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

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

import android.app.Activity;
import android.app.Application;
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 final Intent    SERVICE_INTENT = new Intent();
    private static BeemApplication mBeemApp;
    static {
	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
    }

    /**
     * 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.mApplicationContext = activity.getApplication();
	activity.getResources();
	mBeemApp.onCreate();
	return mBeemApp;
    }

    private Activity                 mActivity;
    public static final String       TAG                 = "BeemApplication";
    private IXmppFacade              mFacade;
    private Context                  mApplicationContext;
    private final List<Message>      mQueue              = new LinkedList<Message>();

    private boolean                  mIsConnected;

    private IXmppConnection          mConnection;

    private final ConnectionListener mConnectionListener = new ConnectionListener();

    private final ServiceConnection  mServConn           = new ServiceConnection() {

	                                                     @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 {
			                                                 synchronized (mQueue) {
			                                                     for (Message msg : mQueue) {
				                                                 msg.sendToTarget();
			                                                     }
			                                                     mQueue.clear();
			                                                 }
		                                                     }
		                                                 } catch (RemoteException e) {
		                                                     Log.e(TAG, "service connection exception", e);
		                                                 }
	                                                     }

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

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

    /**
     * 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);
	    }
	}
    }

    /**
     * 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;
    }

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

    /**
     * Start the beem service.
     */
    public synchronized void startBeemService() {
	if (!mIsConnected) {
	    ConnectionRunnable cRun = new ConnectionRunnable("Connecting...");
	    mBeemApp.mActivity.runOnUiThread(cRun);
	    // 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;
	}
    }

    /**
     * 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 {
	    Log.e(TAG, "ConnectionClosedOnError");
	    // TODO afficher une notification et reafficher le progress dialog
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onConnect() throws RemoteException {
	    // TODO Auto-generated method stub
	    // 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 {
	}

    }

    private class ConnectionRunnable implements Runnable {

	private String mErrorMsg;

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

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

	@Override
	public void run() {
	}

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

    }

    
}
