package com.beem.project.beem.ui;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jivesoftware.smack.util.StringUtils;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;

import com.beem.project.beem.R;
import com.beem.project.beem.service.Contact;
import com.beem.project.beem.service.PresenceAdapter;
import com.beem.project.beem.service.aidl.IBeemRosterListener;
import com.beem.project.beem.service.aidl.IRoster;
import com.beem.project.beem.service.aidl.IXmppFacade;
import com.beem.project.beem.utils.BeemBroadcastReceiver;
import com.beem.project.beem.utils.Status;

/**
 * The contact list activity displays the roster of the user.
 */
public class ContactList extends Activity {

	// private static final String TAG = "CONTACTLIST_ACT";
	private static final Intent SERVICE_INTENT = new Intent();
	private static final int REQUEST_CODE = 1;
	private BeemContactList mAdapterContactList;
	private BeemBanner mAdapterBanner;
	private IRoster mRoster;
	private List<Contact> mListContact = new ArrayList<Contact>();
	private final List<String> mListGroup = new ArrayList<String>();
	private final Map<String, List<Contact>> mContactOnGroup = new HashMap<String, List<Contact>>();
	private String mCurGroup;
	private Handler mHandler;
	private IXmppFacade mXmppFacade;
	private SharedPreferences mSettings;
	private final ServiceConnection mServConn = new BeemServiceConnection();
	private BeemBroadcastReceiver mReceiver;
	private Map<Integer, Bitmap> mIconsMap = new HashMap<Integer, Bitmap>();
	private LayoutInflater mInflater;

	static {
		SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
	}

	/**
	 * Constructor.
	 */
	public ContactList() {

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onCreate(Bundle saveBundle) {
		super.onCreate(saveBundle);
		mSettings = PreferenceManager.getDefaultSharedPreferences(this);
		setContentView(R.layout.contactlist);
		mAdapterContactList = new BeemContactList(this);
		mAdapterBanner = new BeemBanner(this);
		mHandler = new Handler();
		mReceiver = new BeemBroadcastReceiver(mServConn);
		this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
		mInflater = LayoutInflater.from(this);
		prepareIconsStatus();
	}

	/**
	 * Callback for menu creation.
	 * 
	 * @param menu
	 *            the menu created
	 * @return true on success, false otherwise
	 */
	@Override
	public final boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.contact_list, menu);
		return true;
	}

	/**
	 * Callback for menu item selected.
	 * 
	 * @param item
	 *            the item selected
	 * @return true on success, false otherwise
	 */
	@Override
	public final boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.contact_list_menu_settings:
			startActivityForResult(new Intent(this, Settings.class), REQUEST_CODE);
			return true;
		case R.id.contact_list_menu_add_contact:
			startActivity(new Intent(ContactList.this, AddContact.class));
			return true;
		default:
			return false;
		}
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (requestCode == REQUEST_CODE) {
			if (resultCode == RESULT_OK) {
				mListContact.clear();
				stopService(SERVICE_INTENT);
				finish();
				startActivity(new Intent(this, Login.class));
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onStart() {
		super.onStart();
		bindService(SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onStop() {
		super.onStop();
		if (mReceiver.isBinded())
			unbindService(mServConn);
		mReceiver.setBinded(false);
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		this.unregisterReceiver(mReceiver);
	}

	/**
	 * Comparator Contact by Name.
	 */
	class ComparatorContactListByName<T> implements Comparator<T> {
		/**
		 * Constructor.
		 */
		public ComparatorContactListByName() {

		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int compare(T c1, T c2) {
			return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
		}
	}

	/**
	 * Comparator Contact by status and name.
	 */
	class ComparatorContactListByStatusAndName<T> implements Comparator<T> {
		/**
		 * Constructor.
		 */
		public ComparatorContactListByStatusAndName() {

		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int compare(T c1, T c2) {
			if (((Contact) c1).getStatus() < ((Contact) c2).getStatus()) {
				return 1;
			} else if (((Contact) c1).getStatus() > ((Contact) c2).getStatus()) {
				return -1;
			} else
				return ((Contact) c1).getName().compareToIgnoreCase(((Contact) c2).getName());
		}
	}

	/**
	 * Contact List construction.
	 */
	private void buildContactList() {
		if (mCurGroup != null) {
			mListContact = mContactOnGroup.get(mCurGroup);
		}
		sortBeemContactList();
		ListView listView = (ListView) findViewById(R.id.contactlist);
		listView.setOnItemClickListener(new BeemContactListOnClick());
		listView.setOnItemLongClickListener(new BeemContactListOnLongClick());
		listView.setAdapter(mAdapterContactList);
	}

	/**
	 * buildBanner.
	 */
	private void buildBanner() {
		Gallery g = (Gallery) findViewById(R.id.contactlist_banner);
		g.setOnItemClickListener(new OnItemClickGroupName());
		g.setAdapter(mAdapterBanner);
	}

	/**
	 * Event simple click on item of the contact list.
	 */
	public class BeemContactListOnClick implements OnItemClickListener {
		/**
		 * Constructor.
		 */
		public BeemContactListOnClick() {

		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void onItemClick(AdapterView<?> arg0, View v, int pos, long lpos) {
			Contact c = mListContact.get(pos);
			Intent i = new Intent(ContactList.this, Chat.class);
			i.setData(c.toUri());
			startActivity(i);
		}
	}

	/**
	 * Event long click on item of the contact list.
	 */
	public class BeemContactListOnLongClick implements OnItemLongClickListener {
		/**
		 * Constructor.
		 */
		public BeemContactListOnLongClick() {

		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean onItemLongClick(AdapterView<?> arg0, View v, int pos, long lpos) {
			Contact c = mListContact.get(pos);
			ContactDialog dialogContact = new ContactDialog(ContactList.this, c);
			dialogContact.setOwnerActivity(ContactList.this);
			dialogContact.show();
			return true;
		}
	}

	/**
	 * Event simple click on middle groupe name.
	 */
	private class OnItemClickGroupName implements OnItemClickListener {

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

		@Override
		public void onItemClick(AdapterView<?> arg0, View v, int i, long l) {
			mCurGroup = mListGroup.get(i);
			buildContactList();
		}
	}

	/**
	 * Prepare Bitmap Map.
	 */
	private void prepareIconsStatus() {
		mIconsMap.put(Status.CONTACT_STATUS_AVAILABLE, BitmapFactory.decodeResource(getResources(),
				R.drawable.status_available));
		mIconsMap.put(Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT, BitmapFactory.decodeResource(getResources(),
				R.drawable.status_available));
		mIconsMap.put(Status.CONTACT_STATUS_AWAY, BitmapFactory.decodeResource(getResources(), R.drawable.status_away));
		mIconsMap.put(Status.CONTACT_STATUS_BUSY, BitmapFactory.decodeResource(getResources(), R.drawable.status_dnd));
		mIconsMap.put(Status.CONTACT_STATUS_DISCONNECT, BitmapFactory.decodeResource(getResources(),
				R.drawable.status_offline));
		mIconsMap.put(Status.CONTACT_STATUS_UNAVAILABLE, BitmapFactory.decodeResource(getResources(),
				R.drawable.status_requested));
	}

	/**
	 * Sort the contact list.
	 */
	private void sortBeemContactList() {
		Collections.sort(mListContact, new ComparatorContactListByStatusAndName<Contact>());
	}

	/**
	 * Listener on service event.
	 */
	private class BeemRosterListener extends IBeemRosterListener.Stub {
		/**
		 * Constructor.
		 */
		public BeemRosterListener() {

		}

		/**
		 * Refresh the contact list.
		 */
		private class RunnableChange implements Runnable {
			/**
			 * Constructor.
			 */
			public RunnableChange() {

			}

			/**
			 * {@inheritDoc}
			 */
			@Override
			public void run() {
				sortBeemContactList();
				mAdapterContactList.notifyDataSetChanged();
				mAdapterBanner.notifyDataSetChanged();
			}
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void onEntriesAdded(List<String> addresses) throws RemoteException {
			for (String newName : addresses) {
				Contact c = mRoster.getContact(newName);
				mContactOnGroup.get(getString(R.string.contact_list_all_contact)).add(c);
				if (c.getGroups().size() == 0)
					mContactOnGroup.get(getString(R.string.contact_list_no_group)).add(c);
				else {
					for (String group : c.getGroups()) {
						if (!mListGroup.contains(group)) {
							mListGroup.add(mListGroup.size() - 1, group);
							List<Contact> tmplist = new ArrayList<Contact>();
							mContactOnGroup.put(group, tmplist);
						}
						mContactOnGroup.get(group).add(c);
						if (group.equals(mCurGroup) && !mListContact.contains(c))
							mListContact.add(c);
					}
				}
			}
			mHandler.post(new RunnableChange());
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void onEntriesDeleted(List<String> addresses) throws RemoteException {
			for (String cToDelete : addresses) {
				for (Contact c : mListContact) {
					if (c.getJID().equals(cToDelete)) {
						mListContact.remove(c);
						for (String group : mListGroup) {
							mContactOnGroup.get(group).remove(c);
							if (mContactOnGroup.get(group).size() == 0) {
								mListGroup.remove(group);
								mListContact = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
							}
						}
						break;
					}
				}
			}
			mHandler.post(new RunnableChange());
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void onEntriesUpdated(List<String> addresses) throws RemoteException {
			for (String adr : addresses) {
				Contact c = mRoster.getContact(adr);
				if (c.getGroups() != null) {
					if (mContactOnGroup.get(getString(R.string.contact_list_no_group)).contains(c)) {
						mContactOnGroup.get(getString(R.string.contact_list_no_group)).remove(c);
					}
				}
			}
			mHandler.post(new RunnableChange());
		}

		@Override
		public void onEntryDeleteFromGroup(String group, String jid) throws RemoteException {
			List<Contact> tmpContactList = mContactOnGroup.get(group);
			Contact curContact = null;
			for (Contact c : tmpContactList) {
				if (jid.equals(c.getJID())) {
					curContact = c;
					tmpContactList.remove(c);
					c.getGroups().remove(group);
					if (mContactOnGroup.get(group).size() == 0) {
						mContactOnGroup.remove(group);
						mListGroup.remove(group);
						mListContact = mContactOnGroup.get(getString(R.string.contact_list_all_contact));
					}
					break;
				}
			}
			if (curContact != null && curContact.getGroups().size() == 0) {
				mContactOnGroup.get(getString(R.string.contact_list_no_group)).add(curContact);
			}
			mHandler.post(new RunnableChange());
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
			String from = presence.getFrom();
			boolean resfound = false;
			for (Contact curContact : mListContact) {
				if (curContact.getJID().equals(StringUtils.parseBareAddress(from))) {
					String pres = StringUtils.parseResource(from);
					for (String res : curContact.getMRes()) {
						if (res.equals(pres)) {
							resfound = true;
							break;
						}
					}
					curContact.setStatus(mRoster.getPresence(StringUtils.parseBareAddress(presence.getFrom())));
					int status = presence.getStatus();
					if (!resfound && !Status.statusOnline(status))
						curContact.addRes(pres);
					else if (resfound && Status.statusOnline(status))
						curContact.delRes(pres);
					mHandler.post(new RunnableChange());
					return;
				}
			}
		}
	}

	/**
	 * Adapter contact list.
	 */
	private class BeemContactList extends BaseAdapter {

		/**
		 * Constructor.
		 * 
		 * @param context
		 *            context activity.
		 */
		public BeemContactList(final Context context) {
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int getCount() {
			if (mSettings.getBoolean("settings_key_hidden_contact", false)) {
				int res = 0;
				for (Contact c : mListContact) {
					if (Status.statusOnline(c.getStatus()))
						res++;
				}
				return res;
			} else {
				return mListContact.size();
			}
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Object getItem(int position) {
			return position;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public long getItemId(int position) {
			return position;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;
			if (convertView == null) {
				v = mInflater.inflate(R.layout.contactlistcontact, null);
			}
			Contact c = null;
			if (mSettings.getBoolean("settings_key_hidden_contact", false)) {
				int res = 0;
				for (Contact cur : mListContact) {
					if (res == position) {
						c = cur;
						break;
					}
					if (Status.statusOnline(cur.getStatus()))
						res++;
				}
			} else
				c = mListContact.get(position);
			if (mRoster != null) {
				try {
					c = mRoster.getContact(c.getJID());
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
			if (mSettings.getBoolean("settings_key_hidden_contact", false) && !Status.statusOnline(c.getStatus())) {
				v.setVisibility(View.GONE);
			} else {
				bindView(v, c);
			}
			return v;
		}

		/**
		 * Adapte curContact to the view.
		 * 
		 * @param view
		 *            the row view.
		 * @param curContact
		 *            the current contact.
		 */
		private void bindView(View view, Contact curContact) {

			if (curContact != null) {
				ImageView imgV = (ImageView) view.findViewById(R.id.contactliststatus);
				TextView v = (TextView) view.findViewById(R.id.contactlistpseudo);
				imgV.setImageBitmap(mIconsMap.get(curContact.getStatus()));
				v.setText(curContact.getName());
				v = (TextView) view.findViewById(R.id.contactlistmsgperso);
				v.setText(curContact.getMsgState());
			}
		}
	}

	/**
	 * Adapter banner list.
	 */
	public class BeemBanner extends BaseAdapter {
		/**
		 * Constructor.
		 * 
		 * @param c
		 *            context activity.
		 */
		public BeemBanner(final Context c) {
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int getCount() {
			return mListGroup.size();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Object getItem(int position) {
			return position;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public long getItemId(int position) {
			return position;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;
			if (convertView == null) {
				v = mInflater.inflate(R.layout.contactlist_group, null);
			}
			((TextView) v).setText(mListGroup.get(position));
			return v;
		}

	}

	/**
	 * The service connection used to connect to the Beem service.
	 */
	private class BeemServiceConnection implements ServiceConnection {
		private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();

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

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mXmppFacade = IXmppFacade.Stub.asInterface(service);
			mReceiver.setBinded(true);
			try {
				mRoster = mXmppFacade.getRoster();
				if (mRoster != null) {
					mRoster.addRosterListener(mBeemRosterListener);
					List<Contact> tmpContactList = mRoster.getContactList();
					List<String> tmpGroupList = mRoster.getGroupsNames();
					Collections.sort(tmpGroupList);
					if (mListGroup.size() > 0)
						mListGroup.clear();
					mListGroup.add(getString(R.string.contact_list_all_contact));
					mListGroup.addAll(tmpGroupList);
					mListGroup.add(getString(R.string.contact_list_no_group));
					if (tmpGroupList.size() > 0) {
						List<Contact> tmpNoGroup = new ArrayList<Contact>();
						for (String s : tmpGroupList) {
							List<Contact> tmpList = new ArrayList<Contact>();
							for (Contact c : tmpContactList) {
								if (c.getGroups().size() == 0 && !tmpNoGroup.contains(c))
									tmpNoGroup.add(c);
								else if (c.getGroups().contains(s))
									tmpList.add(c);
							}
							mContactOnGroup.put(s, tmpList);
						}
						mContactOnGroup.put(getString(R.string.contact_list_no_group), tmpNoGroup);
					} else {
						mContactOnGroup.put(getString(R.string.contact_list_no_group), tmpContactList);
					}
					mContactOnGroup.put(getString(R.string.contact_list_all_contact), tmpContactList);
					mCurGroup = getString(R.string.contact_list_all_contact);
					buildBanner();
					buildContactList();
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			}

		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			try {
				mRoster.removeRosterListener(mBeemRosterListener);
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			mXmppFacade = null;
			mRoster = null;
			mListContact.clear();
			mListGroup.clear();
			mContactOnGroup.clear();
			mReceiver.setBinded(false);
		}
	}
}
