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

import com.beem.project.beem.R;
import com.beem.project.beem.service.aidl.IBeemRosterListener;
import com.beem.project.beem.utils.PresenceType;
import com.beem.project.beem.utils.Status;

/**
 * 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<Integer, String> mDefaultStatusMessages;
	private RosterListenerAdapter mRosterListener = new RosterListenerAdapter();

	/**
	 * Constructor.
	 * 
	 * @param roster
	 *            the roster to adapt
	 */
	public RosterAdapter(final Roster roster, Context context) {
		mAdaptee = roster;
		roster.addRosterListener(mRosterListener);
		mDefaultStatusMessages = createDefaultStatusMessagesMap(context);
	}

	/**
	 * {@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 {
		RosterEntry contact = mAdaptee.getEntry(user);
		try {
			mAdaptee.createEntry(user, name, groups);
			contact = mAdaptee.getEntry(user);
		} catch (XMPPException e) {
			Log.e(TAG, "Error while adding new contact", e);
			return null;
		}
		if (groups != null) {
			for (String groupStr : groups) {
				RosterGroup group = mAdaptee.getGroup(groupStr);
				if (group == null) {
					group = mAdaptee.createGroup(groupStr);
				}
				try {
					group.addEntry(contact);
				} catch (XMPPException e) {
					e.printStackTrace();
					return null;
				}
			}
		}
		return getContactFromRosterEntry(contact);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void deleteContact(Contact contact) throws RemoteException {
		try {
			RosterEntry entry = mAdaptee.getEntry(contact.getJID());
			mAdaptee.removeEntry(entry);
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void createGroup(String groupname) throws RemoteException {
		try {
			mAdaptee.createGroup(groupname);
		} catch (IllegalArgumentException e) {
			Log.e(TAG, "Error while creating group", e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Contact getContact(String jid) throws RemoteException {
		if (mAdaptee.contains(jid))
			return getContactFromRosterEntry(mAdaptee.getEntry(jid));
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Contact> getContactList() throws RemoteException {
		List<Contact> coList = new ArrayList<Contact>(mAdaptee.getEntries().size());
		for (RosterEntry entry : mAdaptee.getEntries()) {
			coList.add(getContactFromRosterEntry(entry));
		}
		return coList;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<String> getGroupsNames() throws RemoteException {
		Collection<RosterGroup> groups = mAdaptee.getGroups();
		List<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);
	}

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

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

	@Override
	public void addContactToGroup(String groupName, String jid) throws RemoteException {
		createGroup(groupName);
		RosterGroup group = mAdaptee.getGroup(groupName);
		try {
			group.addEntry(mAdaptee.getEntry(jid));
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void removeContactFromGroup(String groupName, String jid) throws RemoteException {
		RosterGroup group = mAdaptee.getGroup(groupName);
		try {
			group.removeEntry(mAdaptee.getEntry(jid));
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Get a contact from a RosterEntry.
	 * 
	 * @param entry
	 *            a roster entry containing information for the contact.
	 * @return a contact for this entry.
	 */
	private Contact getContactFromRosterEntry(RosterEntry entry) {
		String user = entry.getUser();
		Contact c = new Contact(user);
		Presence p = mAdaptee.getPresence(user);
		
		if (p.getStatus() == null || p.getStatus().equals(""))
			p.setStatus(mDefaultStatusMessages.get(Status.getStatusFromPresence(p)));
		c.setStatus(p);
		try {
			c.setGroups(entry.getGroups());
		} catch (NullPointerException e) {
			Log.d(TAG, "Group list not ready");
		}
		c.setName(entry.getName());
		return c;
	}

	/**
	 * Create a map which contains default status messages.
	 * 
	 * @return
	 */
	private Map<Integer, String> createDefaultStatusMessagesMap(Context context) {
		Map<Integer, String> defaultStatusMessages = new HashMap<Integer, String>();
		defaultStatusMessages.put(Status.CONTACT_STATUS_AVAILABLE, context
				.getString(R.string.contact_status_msg_available));
		defaultStatusMessages.put(Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT, context
				.getString(R.string.contact_status_msg_available_chat));
		defaultStatusMessages.put(Status.CONTACT_STATUS_AWAY, context.getString(R.string.contact_status_msg_away));
		defaultStatusMessages.put(Status.CONTACT_STATUS_BUSY, context.getString(R.string.contact_status_msg_dnd));
		defaultStatusMessages.put(Status.CONTACT_STATUS_DISCONNECT, context
				.getString(R.string.contact_status_msg_offline));
		defaultStatusMessages.put(Status.CONTACT_STATUS_UNAVAILABLE, context.getString(R.string.contact_status_msg_xa));

		return (defaultStatusMessages);
	}

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

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

		/**
		 * Event which is fired when an entry is deleted for a group.
		 * 
		 * @param group
		 *            the group the entry was.
		 * @param jid
		 *            the jid of the entry which is deleted.
		 */
		@SuppressWarnings("unused")
		public void onEntryDeleteFromGroup(final String group, final String jid) {
			// Log.i(TAG, "entry delete listener");
			final int n = mRemoteRosListeners.beginBroadcast();
			for (int i = 0; i < n; i++) {
				IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
				try {
					listener.onEntryDeleteFromGroup(group, jid);
				} catch (RemoteException e) {
					Log.w(TAG, "entry delete listener", e);
				}
			}
			mRemoteRosListeners.finishBroadcast();

		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void entriesAdded(Collection<String> addresses) {
			// Log.i(TAG, "Ajout de l'entry " + addresses.size() + " " +
			// addresses.toArray()[0]);
			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.i(TAG, "Update de l'entry " + addresses.size() + " " +
			// addresses.toArray()[0]);
			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, "presence Changed");
			/* redispatch vers les IBeemRosterListener */
			final int n = mRemoteRosListeners.beginBroadcast();

			for (int i = 0; i < n; i++) {
				IBeemRosterListener listener = mRemoteRosListeners.getBroadcastItem(i);
				try {
					Log.d(TAG, ">>> Presence changed.");
					if (presence.getStatus() == null || presence.getStatus().equals("")) {
						presence.setStatus(mDefaultStatusMessages.get(Status.getStatusFromPresence(presence)));
						Log.d(TAG, ">>> Default status added.");
					}
					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 presence entries", e);
				}
			}
			mRemoteRosListeners.finishBroadcast();
		}
	}
}
