package com.beem.project.beem.ui;

import java.util.List;

import org.jivesoftware.smack.util.StringUtils;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.text.util.Linkify;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;

import com.beem.project.beem.BeemService;
import com.beem.project.beem.R;
import com.beem.project.beem.service.Contact;
import com.beem.project.beem.service.Message;
import com.beem.project.beem.service.PresenceAdapter;
import com.beem.project.beem.service.aidl.IBeemRosterListener;
import com.beem.project.beem.service.aidl.IChat;
import com.beem.project.beem.service.aidl.IChatManager;
import com.beem.project.beem.service.aidl.IChatManagerListener;
import com.beem.project.beem.service.aidl.IMessageListener;
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;

/**
 * This activity class provides the view for instant messaging after selecting a
 * correspondant.
 * 
 * @author barbu
 */

public class SendIM extends Activity implements OnClickListener, OnKeyListener {

	private static final String TAG = "SEND_IM";
	private static final Intent SERVICE_INTENT = new Intent();
	static {
		SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
	}

	private IRoster mRoster;
	private EditText mToSend;
	private SendIMDialogSmiley mSmyDialog;
	private SharedPreferences mSet;
	private Handler mHandler;
	private Contact mContact;
	private ImageView mStatusIcon;
	private IChatManager mChatManager;
	private IChatManagerListener mChatManagerListener;
	private IMessageListener mMessageListener;
	private IChat mChat;
	private TextView mText;
	private TextView mLogin;
	private ScrollView mScrolling;
	private char mSpeak;

	private final ServiceConnection mServConn = new BeemServiceConnection();
	private IXmppFacade mXmppFacade;
	private TextView mStatusText;
	private BeemBroadcastReceiver mReceiver;

	/**
	 * Constructor.
	 */
	public SendIM() {
		super();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onClick(View view) {
		sendText();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onCreate(Bundle saveBundle) {
		super.onCreate(saveBundle);
		// setTheme(R.style.OldTheme);
		setContentView(R.layout.sendim);
		mHandler = new Handler();
		mChatManagerListener = new OnChatListener();
		mMessageListener = new OnMessageListener();
		mToSend = (EditText) findViewById(R.id.userText);
		mSet = getSharedPreferences("lol", MODE_PRIVATE);
		mSmyDialog = new SendIMDialogSmiley(this, mSet);
		mToSend.setOnClickListener(this);
		mToSend.setOnKeyListener(this);
		mStatusIcon = (ImageView) findViewById(R.id.sendimstatus);
		mLogin = (TextView) findViewById(R.id.sendimlogin);
		mContact = new Contact(getIntent().getData());
		mText = (TextView) findViewById(R.id.sendimlist);
		mScrolling = (ScrollView) findViewById(R.id.sendimscroll);
		mStatusText = (TextView) findViewById(R.id.sendimstatusmsg);
		mReceiver = new BeemBroadcastReceiver(mServConn);
		this.registerReceiver(mReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.sendimmenu, menu);
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onDestroy() {
		super.onDestroy();
		this.unregisterReceiver(mReceiver);
		if (mChatManager != null) {
			try {
				mChatManager.removeChatCreationListener(mChatManagerListener);
				// TODO trouver quand detruire le chat
				// mChatManager.destroyChat(mChat);
			} catch (RemoteException e) {
				Log.e(TAG, "mchat manager and SendIM destroy", e);
			}
		}
	}

	/**
	 * Update the status icon of our contact.
	 */
	protected void updateStatusIcon() {
		Drawable statusIcon = null;
		switch (mContact.getStatus()) {
		case Status.CONTACT_STATUS_AVAILABLE:
			statusIcon = getResources().getDrawable(R.drawable.status_available);
			break;
		case Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT:
			statusIcon = getResources().getDrawable(R.drawable.status_available);
			break;
		case Status.CONTACT_STATUS_AWAY:
			statusIcon = getResources().getDrawable(R.drawable.status_away);
			break;
		case Status.CONTACT_STATUS_BUSY:
			statusIcon = getResources().getDrawable(R.drawable.status_dnd);
			break;
		case Status.CONTACT_STATUS_DISCONNECT:
			statusIcon = getResources().getDrawable(R.drawable.status_offline);
			break;
		case Status.CONTACT_STATUS_UNAVAILABLE:
			statusIcon = getResources().getDrawable(R.drawable.status_requested);
			break;
		default:
			statusIcon = getResources().getDrawable(R.drawable.status_error);
			break;
		}
		Log.d("SENDIM -- UPDATE STATUS ICON", "Contact current status: " + mContact.getStatus() + "");
		mStatusIcon.setImageDrawable(statusIcon);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean onKey(View v, int keyCode, KeyEvent event) {
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_CENTER:
			case KeyEvent.KEYCODE_ENTER:
				sendText();
				return true;
			default:
				return false;
			}
		}
		return false;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		mContact = new Contact(intent.getData());
		try {
			if (mRoster != null)
				mContact = mRoster.getContact(mContact.getJID());
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.sendim_smiley:
			mSmyDialog.show();
			return true;
		case R.id.sendim_call:
			// TODO start the jingle call
			// Bug a besoin du jid complet (resource compris)
			try {
				mXmppFacade.call(mContact.getJID());
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return true;
		default:
			return false;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onStop() {
		super.onStop();
		try {
			if (mChat != null)
				mChat.setOpen(false);
		} catch (RemoteException e) {
			Log.d(TAG, "Error while closing chat", e);
		}
		if (mReceiver.isBinded()) {
			unbindService(mServConn);
			mReceiver.setBinded(false);
		}
		mRoster = null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onStart() {
		super.onStart();
		bindService(new Intent(this, BeemService.class), mServConn, BIND_AUTO_CREATE);
		mScrolling.fullScroll(ScrollView.FOCUS_DOWN);
		try {
			if (mRoster != null)
				mContact = mRoster.getContact(mContact.getJID());
		} catch (RemoteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * The service connection used to connect with the BeemService.
	 */
	private final 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 {
				if (mChatManager == null) {
					mChatManager = mXmppFacade.getChatManager();
					mChatManager.addChatCreationListener(mChatManagerListener);
				}
				mRoster = mXmppFacade.getRoster();
				if (mRoster != null) {
					mRoster.addRosterListener(mBeemRosterListener);
					// mContact.setStatus(mRoster.getPresence(mContact.getJID()));
					if (mRoster.getContact(mContact.getJID()) != null) {
						mContact = mRoster.getContact(mContact.getJID());
						mStatusText.setText(mContact.getMsgState());
						mLogin.setText(mContact.getName());
					}
					updateStatusIcon();
				}
				switchChat(mContact);
			} catch (RemoteException e) {
				Log.e(TAG, "Error during chat manager creation", e);
			}
		}

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

	/**
	 * Send a message to the contact over the XMPP connection. Also display it
	 * on activity view. TODO : Gerer l'exception si la connexion se coupe
	 * pendant la conversation
	 */
	private void sendText() {
		String text = mToSend.getText().toString();
		if (!"".equals(text)) {
			Message msg = new Message(mContact.getJID(), Message.MSG_TYPE_CHAT);
			msg.setBody(text);
			try {
				mChat.sendMessage(msg);
				if (mSpeak != 1)
					mText.append(getString(R.string.SendIMYouSay, text));
				else
					mText.append(getString(R.string.SendIMSameSpeaker, text));
				mToSend.setText(null);
				mScrolling.fullScroll(ScrollView.FOCUS_DOWN);
				mToSend.requestFocus();
				mSpeak = 1;
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * Show the message history.
	 * 
	 * @param messages
	 *            list of message to display
	 */
	private void showMessageList(List<Message> messages) {
		mText.setText("");
		mSpeak = 0;
		for (Message message : messages) {
			String from = message.getFrom();
			if (from == null) {
				if (mSpeak != 1)
					mText.append(getString(R.string.SendIMYouSay, message.getBody()));
				else
					mText.append(getString(R.string.SendIMYouSay, message.getBody()));
				mSpeak = 1;
			} else {
				if (mSpeak != 2)
					mText.append(getString(R.string.SendIMSays, mContact.getName(), message.getBody()));
				else
					mText.append(getString(R.string.SendIMSameSpeaker, message.getBody()));
				mSpeak = 2;
			}
		}
		mScrolling.fullScroll(ScrollView.FOCUS_DOWN);
	}

	/**
	 * Change the correspondant of the chat.
	 * 
	 * @param newContact
	 *            New contact to chat with
	 * @throws RemoteException
	 *             if an errors occurs in the connection with the service
	 */
	private void switchChat(Contact newContact) throws RemoteException {
		if (mChat != null)
			mChat.setOpen(false);
		mChat = mChatManager.createChat(newContact, mMessageListener);
		showMessageList(mChat.getMessages());
		mChat.setOpen(true);
		mChatManager.deleteChatNotification(mChat);
		mContact = newContact;
		mToSend.requestFocus();
	}

	/**
	 * Listener for chat creation. (maybe not necessary).
	 * 
	 * @author darisk
	 */
	private class OnChatListener extends IChatManagerListener.Stub {

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

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void chatCreated(IChat chat, boolean locally) throws RemoteException {
			Log.i("LOG", "chatCreated");
		}

	}

	/**
	 * Listener use to change the status displayed.
	 */
	private class BeemRosterListener extends IBeemRosterListener.Stub {

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

		@Override
		public void onEntriesAdded(List<String> addresses) throws RemoteException {
			// TODO Auto-generated method stub

		}

		@Override
		public void onEntriesDeleted(List<String> addresses) throws RemoteException {
			// TODO Auto-generated method stub

		}

		@Override
		public void onEntriesUpdated(List<String> addresses) throws RemoteException {
			// TODO Auto-generated method stub

		}

		@Override
		public void onEntryDeleteFromGroup(String group, String jid) throws RemoteException {
			// TODO Auto-generated method stub

		}

		@Override
		public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
			if (mContact.getJID().equals(StringUtils.parseBareAddress(presence.getFrom()))) {
				mContact.setStatus(mRoster.getPresence(StringUtils.parseBareAddress(presence.getFrom())));
				Log.d("SENDIM -- onPresenceChanged", "Contact current status: " + mContact.getStatus() + "");
				mHandler.post(new RunnableChange());
			}
		}

		/**
		 * Runnable to change the status displayed.
		 */
		private class RunnableChange implements Runnable {

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

			@Override
			public void run() {
				updateStatusIcon();
				mStatusText.setText(mContact.getMsgState());
				Linkify.addLinks(mStatusText, Linkify.WEB_URLS);
			}
		}
	}

	/**
	 * Listener for new chat messages.
	 * 
	 * @author darisk
	 */
	private class OnMessageListener extends IMessageListener.Stub {

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

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void processMessage(IChat chat, Message msg) throws RemoteException {

			if (chat != mChat)
				return;
			final Message m = msg;
			Log.d("Message Type", "Le type du message : " + m.getType());
			mHandler.post(new Runnable() {

				@Override
				public void run() {
					if (m.getBody() != null) {
						if (m.getType() == Message.MSG_TYPE_ERROR) {
							mText.append(getString(R.string.SendIMErrorMsg, m.getBody()));
							mSpeak = 0;
						} else if (mSpeak == 2)
							mText.append(getString(R.string.SendIMSameSpeaker, m.getBody()));
						else
							mText.append(getString(R.string.SendIMSays, mContact.getName(), m.getBody()));
						mScrolling.fullScroll(ScrollView.FOCUS_DOWN);
						mToSend.requestFocus();
						mSpeak = 2;
					}
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void stateChanged(IChat chat) throws RemoteException {
			if (chat != mChat)
				return;
			Log.d("ChatState", "Action du correspondant : <--- " + chat.getState() + " --->");
			mHandler.post(new Runnable() {

				@Override
				public void run() {
				}
			});
		}
	}
}
