/**
 *
 */
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 android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

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

/**
 * 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());
		c.setName(entry.getName());
		mContacts.put(user, c);
	    }
	}
    }

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

    /**
     * {@inheritDoc}
     */
    @Override
    public Contact addContact(String user, String name, String[] groups) throws RemoteException {
	Contact res = null;
	RosterEntry contact = mAdaptee.getEntry(user);
	if (contact != null) {
	    res = mContacts.get(user);
	    if (groups != null) {
		for (String groupStr : groups) {
		    boolean found = false;
		    for (RosterGroup group : mAdaptee.getGroups()) {
			if (group.getName().equals(groupStr) && !group.contains(contact)) {
			    try {
				group.addEntry(contact);
				res.addGroup(groupStr);
				found = true;
			    } catch (XMPPException e) {
				e.printStackTrace();
			    }
			}
		    }
		    if (!found) {
			try {
			    mAdaptee.createGroup(groupStr).addEntry(contact);
			} catch (XMPPException e) {
			    e.printStackTrace();
			} catch (IllegalArgumentException e) {
			}
			res.addGroup(groupStr);
		    }
		}
	    }
	} else {
	    try {
		mAdaptee.createEntry(user, name, groups);
		res = new Contact(user);
		mContacts.put(user, res);
		if (groups != null) {
		    for (String groupStr : groups) {
			try {
			    mAdaptee.createGroup(groupStr);
			} catch (IllegalArgumentException e) {
			    // e.printStackTrace();
			}
			res.addGroup(groupStr);
		    }
		}
	    } catch (XMPPException e) {
		Log.e(TAG, "Error while adding new contact", e);
		return null;
	    }
	}
	return res;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteContact(Contact contact, String group) throws RemoteException {
	mContacts.get(contact.getJID()).delGroup(group);
	try {
	    if (mAdaptee != null) {
		RosterEntry entry = mAdaptee.getEntry(contact.getJID());
		if (entry != null)
		    mAdaptee.getGroup(group).removeEntry(entry);
	    }
	} catch (XMPPException e) {
	    e.printStackTrace();
	}
    }

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

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

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

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

    /**
     * 
     */
    @Override
    public void setContactName(String jid, String name) throws RemoteException {
	mContacts.get(jid).setName(name);
	mAdaptee.getEntry(jid).setName(name);
    }

    @Override
    public PresenceAdapter getPresence(String jid) throws RemoteException {
	return new PresenceAdapter(mAdaptee.getPresence(jid));
    }

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

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

	/**
	 * {@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>();
	    tab.addAll(addresses);
	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    listener.onEntriesAdded(tab);
		} catch (RemoteException e) {
		    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>();
	    tab.addAll(addresses);
	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    listener.onEntriesDeleted(tab);
		} catch (RemoteException e) {
		    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>();
	    tab.addAll(addresses);
	    for (int i = 0; i < n; i++) {
		IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
		try {
		    listener.onEntriesUpdated(tab);
		} catch (RemoteException e) {
		    Log.w(TAG, "Error while updating roster entries", e);
		}
	    }
	    mRemoteRosListeners.finishBroadcast();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void presenceChanged(Presence presence) {
	    String user = StringUtils.parseBareAddress(presence.getFrom());
	    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());
	    c.setName(mAdaptee.getEntry(user).getName());
	    /* 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();
	}
    }

}
