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() {
		}
	    });
	}
    }
}
