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

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
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 {

	/**
	 * 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>();
			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) {
			Log.i(TAG, "Changement de Presence");
			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());
			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();
		}
	}

	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);
			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);
				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 createGroup(String groupname) throws RemoteException {
		mAdaptee.createGroup(groupname);
	}

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

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

}
