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

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.util.StringUtils;

import com.beem.project.beem.service.aidl.IBeemRosterListener;

import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

/**
 * This class implement a Roster adapter for BEEM.
 * @author darisk
 */
public class RosterAdapter extends com.beem.project.beem.service.aidl.IRoster.Stub {

    private static final String TAG = "RosterAdapter";
    private Roster mAdaptee;
    private RemoteCallbackList<IBeemRosterListener> mRemoteRosListeners = new RemoteCallbackList<IBeemRosterListener>();
    private Map<String, Contact> mContacts = new HashMap<String, Contact>();

    private RosterListenerAdapter mRosterListener = new RosterListenerAdapter();

    /**
     * Constructor.
     * @param roster the roster to adapt
     */
    public RosterAdapter(final Roster roster) {
	mAdaptee = roster;
	roster.addRosterListener(mRosterListener);
	for (RosterEntry entry : roster.getEntries()) {
	    String user = StringUtils.parseBareAddress(entry.getUser());
	    if (!mContacts.containsKey(user)) {
		Contact c = new Contact(user);
		c.setStatus(roster.getPresence(user));
		c.setGroups(entry.getGroups());
		mContacts.put(user, c);
	    }
	}
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void createGroup(String groupname) throws RemoteException {
	mAdaptee.createGroup(groupname);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Contact addContact(String user, String name, String[] groups) throws RemoteException {
	try {
	    mAdaptee.createEntry(user, name, groups);
	    Contact res = new Contact(user);
	    mContacts.put(user, res);
	    for (String group : groups) {
		mAdaptee.createGroup(group);
		res.addGroup(group);
	    }
	    return res;
	} catch (XMPPException e) {
	    Log.e(TAG, "Error while adding new contact", e);
	    return null;
	}
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteContact(Contact contact) throws RemoteException {
	mContacts.remove(contact.getJID());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addConnectionListener(IBeemRosterListener listen) throws RemoteException {
	if (listen != null)
	    mRemoteRosListeners.register(listen);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeConnectionListener(IBeemRosterListener listen) throws RemoteException {
	if (listen != null)
	    mRemoteRosListeners.unregister(listen);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Contact getContact(String jid) throws RemoteException {
	return mContacts.get(jid);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<Contact> getContactList() throws RemoteException {
	List<Contact> res = new ArrayList<Contact>();
	res.addAll(mContacts.values());
	return res;
    }

    /**
     * Listener for the roster events. It will call the remote listeners registered.
     * @author darisk
     */
    private class RosterListenerAdapter implements RosterListener {

	/**
	 * Constructor.
	 */
	public RosterListenerAdapter() {
	    // TODO Auto-generated constructor stub
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void entriesAdded(Collection<String> addresses) {
	    Log.i(TAG, "Ajout de l'entry");
	    final int n = mRemoteRosListeners.beginBroadcast();

	    List<String> tab = new ArrayList<String>();
	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    tab.addAll(addresses);
		    listener.onEntriesAdded(tab);
		} catch (RemoteException e) {
		    // The RemoteCallbackList will take care of removing the
		    // dead listeners.
		    Log.w(TAG, "Error while adding roster entries", e);
		}
	    }
	    mRemoteRosListeners.finishBroadcast();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void entriesDeleted(Collection<String> addresses) {
	    Log.i(TAG, "Suppression de l'entry");
	    final int n = mRemoteRosListeners.beginBroadcast();

	    List<String> tab = new ArrayList<String>();
	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    tab.addAll(addresses);
		    listener.onEntriesDeleted(tab);
		} catch (RemoteException e) {
		    // The RemoteCallbackList will take care of removing the
		    // dead listeners.
		    Log.w(TAG, "Error while deleting roster entries", e);
		}
	    }
	    mRemoteRosListeners.finishBroadcast();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void entriesUpdated(Collection<String> addresses) {
	    Log.d(TAG, "Update de l'entry");
	    final int n = mRemoteRosListeners.beginBroadcast();

	    List<String> tab = new ArrayList<String>();
	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    tab.addAll(addresses);
		    listener.onEntriesUpdated(tab);
		} catch (RemoteException e) {
		    // The RemoteCallbackList will take care of removing the
		    // dead listeners.
		    Log.w(TAG, "Error while updating roster entries", e);
		}
	    }
	    mRemoteRosListeners.finishBroadcast();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void presenceChanged(Presence presence) {
	    Log.i(TAG, "Changement de Presence");
	    /* gestion du roster coter sedirvice */
	    String user = StringUtils.parseBareAddress(presence.getFrom());
	    Log.d(TAG, "User : " + user);
	    Contact c = mContacts.get(StringUtils.parseBareAddress(user));
	    if (c == null) {
		c = new Contact(user);
		mContacts.put(user, c);
	    }
	    c.addRes(StringUtils.parseResource(presence.getFrom()));
	    c.setStatus(mAdaptee.getPresence(presence.getFrom()));
	    c.setMsgState(presence.getStatus());
	    /* redispatch vers les IBeemRosterListener */
	    final int n = mRemoteRosListeners.beginBroadcast();

	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    listener.onPresenceChanged(new PresenceAdapter(presence));
		} catch (RemoteException e) {
		    // The RemoteCallbackList will take care of removing the
		    // dead listeners.
		    Log.w(TAG, "Error while updating roster entries", e);
		}
	    }
	    mRemoteRosListeners.finishBroadcast();
	}
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<String> getGroupsNames() throws RemoteException {
	Collection<RosterGroup> groups = mAdaptee.getGroups();
	ArrayList<String> result = new ArrayList<String>(groups.size());
	for (RosterGroup rosterGroup : groups) {
	    result.add(rosterGroup.getName());
	}
	return result;
    }

}
