package com.beem.project.beem.ui;

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

import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.ChatState;

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.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
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;

public class Chat extends Activity {

    private static final String TAG = "Chat";
    private static final Intent SERVICE_INTENT = new Intent();
    static {
	SERVICE_INTENT.setComponent(new ComponentName("com.beem.project.beem", "com.beem.project.beem.BeemService"));
    }
    
    private Handler mHandler = new Handler();
    
    private IRoster mRoster;
    private Contact mContact;

    private TextView mContactNameTextView;
    private TextView mContactStatusMsgTextView;
    private ImageView mContactStatusIcon;
    private ListView mMessagesListView;
    private EditText mInputField;
    private Map<Integer, Bitmap> mStatusIconsMap = new HashMap<Integer, Bitmap>();
    
    private List<MessageText> mListMessages = new ArrayList<MessageText>();
    
    private IChat mChat;
    private IChatManager mChatManager;
    private IChatManagerListener mChatManagerListener;
    private IMessageListener mMessageListener;
    private MessagesListAdapter mMessagesListAdapter;
    
    private final ServiceConnection mConn = new BeemServiceConnection();
    private BeemBroadcastReceiver mBroadcastReceiver;
    
    
    /**
     * Constructor.
     */
    public Chat() {
	super();
    }
    
    /**
     * {@inheritDoc}.
     */
    protected void onCreate(Bundle savedBundle) {
	super.onCreate(savedBundle);
	
	Log.v(TAG, "BEGIN onCreate.");
	setContentView(R.layout.chat);
	mMessagesListAdapter = new MessagesListAdapter(this);
	
	mHandler = new Handler();
	
	// Listeners
	mMessageListener = new OnMessageListener();
	mChatManagerListener = new OnChatManagerListener();

	mBroadcastReceiver = new BeemBroadcastReceiver(mConn);
	this.registerReceiver(mBroadcastReceiver, new IntentFilter(BeemBroadcastReceiver.BEEM_CONNECTION_CLOSED));
	
	// UI
	mContactNameTextView = (TextView) findViewById(R.id.chat_contact_name);
	mContactStatusMsgTextView = (TextView) findViewById(R.id.chat_contact_status_msg);
	mContactStatusIcon = (ImageView) findViewById(R.id.chat_contact_status_icon);
	mMessagesListView = (ListView) findViewById(R.id.chat_messages);
	mMessagesListView.setAdapter(mMessagesListAdapter);
	mInputField = (EditText) findViewById(R.id.chat_input);
	
	prepareIconsStatus();
	
	Log.v(TAG, "END onCreate.");
    }
    
    /**
     * {@inheritDoc}.
     */
    protected void onDestroy() {
	super.onDestroy();
	
	Log.v(TAG, "BEGIN onDestroy.");
	this.unregisterReceiver(mBroadcastReceiver);
	if (mChatManager != null) {
	    try {
		mChatManager.removeChatCreationListener(mChatManagerListener);
	    } catch (RemoteException e) {
		Log.e(TAG, e.getMessage());
	    }
	}
	Log.v(TAG, "END onDestroy.");
    }
    
    /**
     * {@inheritDoc}.
     */
    protected void onStart() {
	super.onStart();
	
	Log.v(TAG, "BEGIN onStart.");
	bindService(new Intent(this, BeemService.class), mConn, BIND_AUTO_CREATE);
	Log.v(TAG, "END onStart.");
    }
    
    /**
     * {@inheritDoc}.
     */
    protected void onStop() {	
	super.onStop();
	
	Log.v(TAG, "BEGIN onStop.");
	if (mChat != null) {
	    try {
		mChat.setOpen(false);
	    } catch (RemoteException e) {
		Log.e(TAG, e.getMessage());
	    }
	}
	if (mBroadcastReceiver.isBinded()) {
	    unbindService(mConn);
	}
	Log.v(TAG, "END onStop.");
    }
    
    /**
     * {@inheritDoc}.
     */
    protected void onResume() {
	super.onResume();
    }
  
    /**
     * {@inheritDoc}.
     */
    protected void onPause() {
	super.onPause();
    }
    
    /**
     * {@inheritDoc}.
     */
    protected void onNewIntent(Intent intent) {
	super.onNewIntent(intent);
	
	Log.v(TAG, "BEGIN onNewIntent.");
	mContact = new Contact(intent.getData());
	updateContactInformations();
	Log.v(TAG, "END onNewIntent.");
    }
    
    /**
     * 
     * @author Jamu
     *
     */
    private final class BeemServiceConnection implements ServiceConnection {
	
	private IXmppFacade mXmppFacade;
	private final BeemRosterListener mBeemRosterListener = new BeemRosterListener();

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
	    Log.v(TAG, "BEGIN onServiceConnected.");
	    mXmppFacade = IXmppFacade.Stub.asInterface(service);
	    mBroadcastReceiver.setBinded(true);
	    try {
		mChatManager = mXmppFacade.getChatManager();
		
		mRoster = mXmppFacade.getRoster();
		mRoster.addRosterListener(mBeemRosterListener);
		
		mChat = mChatManager.createChat((mContact = new Contact(getIntent().getData())), mMessageListener);
		mChat.setOpen(true);
		mChatManager.deleteChatNotification(mChat);
		
		updateContactInformations();
		updateContactStatusIcon();
	    } catch (RemoteException e) {
		Log.e(TAG, e.getMessage());
	    }
	    Log.v(TAG, "END onServiceConnected.");
	}
	
	@Override
	public void onServiceDisconnected(ComponentName name) {
	    Log.v(TAG, "BEGIN onServiceDisconnected.");
	    mXmppFacade = null;
	    mBroadcastReceiver.setBinded(false);
	    try {
		mRoster.removeRosterListener(mBeemRosterListener);
	    } catch (RemoteException e) {
		Log.e(TAG, e.getMessage());
	    }
	    Log.v(TAG, "END onServiceDisconnected.");
	}
    }
    

    /**
     * 
     * @author Jamu
     *
     */
    private class BeemRosterListener extends IBeemRosterListener.Stub {

	@Override
	public void onEntriesAdded(List<String> addresses) throws RemoteException {
	    // Nothing to do
	}

	@Override
	public void onEntriesDeleted(List<String> addresses) throws RemoteException {
	    // Nothing to do
	}

	@Override
	public void onEntriesUpdated(List<String> addresses) throws RemoteException {  
	    // Nothing to do
	}

	@Override
	public void onEntryDeleteFromGroup(String group, String jid) throws RemoteException {
	    // Nothing to do
	}

	@Override
	public void onPresenceChanged(PresenceAdapter presence) throws RemoteException {
	    Log.v(TAG, "BEGIN onPresenceChanged.");
	    if (mContact.getJID().equals(StringUtils.parseBareAddress(presence.getFrom()))) {
		mHandler.post(new Runnable() {
		    @Override
		    public void run() {
			updateContactInformations();
			updateContactStatusIcon();
		    }
		});
	    }
	    Log.v(TAG, "END onPresenceChanged.");
	}
    }
    
    /**
     * 
     * @author Jamu
     *
     */
    private class OnMessageListener extends IMessageListener.Stub {

	@Override
	public void processMessage(IChat chat, final Message msg) throws RemoteException {
	    Log.v(TAG, "BEGIN processMessage.");
	    mHandler.post(new Runnable() {

		@Override
		public void run() {
		    if (msg.getBody() != null && msg.getType() != Message.MSG_TYPE_ERROR) {
			mListMessages.add(new MessageText(msg.getFrom(), msg.getBody()));
			mMessagesListAdapter.notifyDataSetChanged();
		    }
		}
	    });
	    Log.v(TAG, "END processMessage.");
	}

	@Override
	public void stateChanged(IChat chat) throws RemoteException {
	    Log.v(TAG, "BEGIN stateChanged.");
	    mHandler.post(new Runnable() {
		
		@Override
		public void run() {
		}
	    });
	    Log.v(TAG, "END stateChanged.");
	}

    }
    
    /**
     * 
     * @author Jamu
     *
     */
    private class OnChatManagerListener extends IChatManagerListener.Stub {
	/**
	 * Constructor.
	 */
	public OnChatManagerListener() {
	    Log.v(TAG, "OnChatManagerListener constructor.");
	}
	
	@Override
	public void chatCreated(IChat chat, boolean locally) throws RemoteException {
	    Log.i(TAG, "Chat has been created.");
	}
    }
    
    /**
     * Update the contact informations.
     *
     */
    private void updateContactInformations() {
	Log.v(TAG, "BEGIN updateContactInformations.");
	// Check for a contact name update
	if (!(mContactNameTextView.getText().toString().equals(mContact.getName())))
	    mContactNameTextView.setText(mContact.getName());
	
	// Check for a contact status message update
	if (!(mContactStatusMsgTextView.getText().toString().equals(mContact.getMsgState()))) {
	    Log.d(TAG, "Setting status message - " + mContact.getMsgState());
	    if (mContact.getMsgState() == null)
		mContactStatusMsgTextView.setText("Message de status de test de la mort tres tres long, voir meme trop long!");
	    else
		mContactStatusMsgTextView.setText(mContact.getMsgState());
	    Linkify.addLinks(mContactStatusMsgTextView, Linkify.WEB_URLS);
	}
	Log.v(TAG, "END updateContactInformations.");
    }
    
    /**
     * Update the contact status icon.
     */
    private void updateContactStatusIcon() {
	Log.v(TAG, "BEGIN updateContactStatusIcon.");
	mContactStatusIcon.setImageBitmap(mStatusIconsMap.get(mContact.getStatus()));
	Log.v(TAG, "END updateContactStatusIcon.");
    }
    
    /**
     * Prepare the status icons map.
     */
    private void prepareIconsStatus() {
	mStatusIconsMap.put(Status.CONTACT_STATUS_AVAILABLE, BitmapFactory.decodeResource(getResources(), R.drawable.status_available));
	mStatusIconsMap.put(Status.CONTACT_STATUS_AVAILABLE_FOR_CHAT, BitmapFactory.decodeResource(getResources(), R.drawable.status_available));
	mStatusIconsMap.put(Status.CONTACT_STATUS_AWAY, BitmapFactory.decodeResource(getResources(), R.drawable.status_away));
	mStatusIconsMap.put(Status.CONTACT_STATUS_BUSY, BitmapFactory.decodeResource(getResources(), R.drawable.status_dnd));
	mStatusIconsMap.put(Status.CONTACT_STATUS_DISCONNECT, BitmapFactory.decodeResource(getResources(), R.drawable.status_offline));
	mStatusIconsMap.put(Status.CONTACT_STATUS_UNAVAILABLE, BitmapFactory.decodeResource(getResources(), R.drawable.status_requested));
    }
    
    private class MessagesListAdapter extends BaseAdapter {

        private Context mContext;
	
        public MessagesListAdapter(Context context) {
            mContext = context;
        }

        public int getCount() {
            return mListMessages.size();
        }

        public Object getItem(int position) {
            return position;
        }

        public long getItemId(int position) {
            return position;
        }

        public View getView(int position, View convertView, ViewGroup parent) {
            MessageView sv;
            if (convertView == null) {
                sv = new MessageView(mContext, mListMessages.get(position).getName(),
                    	mListMessages.get(position).getMessage());
            } else {
                sv = (MessageView) convertView;
                sv.setName(mListMessages.get(position).getName());
                sv.setMessage(mListMessages.get(position).getMessage());
            }

            return sv;
        }
    }
    
    private class MessageText {
	private String mName;
	private String mMessage;
	
	public MessageText(String name, String message) {
	    mName = name;
	    mMessage = message;
	}
	
	public String getName() {
	    return mName;
	}
	
	public String getMessage() {
	    return mMessage;
	}
	
	public void setName(String name) {
	    mName = name;
	}
	
	public void setMessage(String message) {
	    mMessage = message;
	}
    }
    
    /**
     * We will use a MessageView to display each message.
     */
    private class MessageView extends LinearLayout {
        private TextView mName;
        private TextView mMessage;
	
        /**
         * Constructor.
         * @param context
         * @param name
         * @param message
         */
        public MessageView(Context context, String name, String message) {
            super(context);

            this.setOrientation(VERTICAL);

            mName = new TextView(context);
            mName.setText(name);
            addView(mName, new LinearLayout.LayoutParams(
                    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

            mMessage = new TextView(context);
            mMessage.setText(message);
            addView(mMessage, new LinearLayout.LayoutParams(
                    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
        }

        /**
         * Convenience method to set the title of a MessageView
         */
        public void setName(String name) {
            mName.setText(name);
        }

        /**
         * Convenience method to set the dialogue of a MessageView
         */
        public void setMessage(String message) {
            mMessage.setText(message);
        }
    }
}
