/*
    BEEM is a videoconference application on the Android Platform.

    Copyright (C) 2009 by Frederic-Charles Barthelery,
                          Jean-Manuel Da Silva,
                          Nikita Kozlov,
                          Philippe Lago,
                          Jean Baptiste Vergely,
                          Vincent Veronis.

    This file is part of BEEM.

    BEEM is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    BEEM is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with BEEM.  If not, see <http://www.gnu.org/licenses/>.

    Please send bug reports with examples or suggestions to
    contact@beem-project.com or http://dev.beem-project.com/

 */

package com.beem.project.beem;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smackx.XmppStreamHandler;
import org.jivesoftware.smackx.bookmark.BookmarkManager;
import org.jivesoftware.smackx.muc.DiscussionHistory;
import org.jivesoftware.smackx.muc.MultiUserChat;

import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;

import com.beem.project.beem.providers.MUCs;
import com.beem.project.beem.providers.Messages;
import com.beem.project.beem.utils.Status;

/**
 * Connection.
 * @author marseille
 */
public class BeemConnection {

    private static final String TAG = BeemConnection.class.getName();

    private static final int SMACK_PRIORITY_MIN = -128;
    private static final int SMACK_PRIORITY_MAX = 128;

    private final Context mContext;
    private final XMPPConnection mXMPPConnection;
    private final SharedPreferences mSharedPreferences;
    private final BeemServiceBroadcastReceiver mOnOffReceiver = new BeemServiceBroadcastReceiver();
    private final BeemInfoConnection mInfoConnection;

    private XmppStreamHandler mStreamHandler;
    private String mErrorMsg;

    private String mPreviousStatus;

    private int mPreviousMode;

    private int mPreviousPriority;

    /**
     * Constructor.
     */
    BeemConnection(Context c, String accountName) {
	mContext = c;
	mSharedPreferences = c.getSharedPreferences(accountName, BeemService.MODE_PRIVATE);
	mInfoConnection = new BeemInfoConnection(mSharedPreferences);
	mXMPPConnection = new XMPPConnection(mInfoConnection.getConnectionConfiguration());
    }

    public void finalize() {
	mXMPPConnection.disconnect();
	editSharedPreferences(BeemApplication.STATUS_KEY, Status.CONTACT_STATUS_DISCONNECT);
    }

    public final boolean connect() {
	try {
	    mXMPPConnection.connect();
	    mXMPPConnection.login(mInfoConnection.getLogin(), mInfoConnection.getPassword(),
		mInfoConnection.getResource());
	} catch (XMPPException e) {
	    setErrorMessage(e, "Error while connecting");
	    return false;
	}
	mStreamHandler = new XmppStreamHandler(mXMPPConnection);
	mStreamHandler.notifyInitialLogin();
	mXMPPConnection.addPacketListener(new BeemPacketListener(mContext, mXMPPConnection), new BeemPacketFilter());
	return true;
    }

    public void handleXMPPMessage(String accountName, android.os.Message msg) {

	Bundle b = (Bundle) msg.obj;
	String to = b.getString(BeemIntent.EXTRA_JID);
	String body = b.getString(BeemIntent.EXTRA_MESSAGE);

	Message send = new Message();
	send.setFrom(accountName);
	send.setThread(""); //TODO: set ThreadID
	send.setTo(to);
	send.setBody(body);
	send.setType(Message.Type.chat);
	// TODO gerer les messages contenant des XMPPError
	try {
	    mXMPPConnection.sendPacket(send);
	} catch (IllegalStateException e) {
	    Log.e(TAG, "Message not send", e);
	    return;
	}
	SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	ContentValues values = new ContentValues();
	values.put(Messages.FROM, accountName);
	values.put(Messages.MESSAGE_ID, "message id");
	values.put(Messages.TO, to);
	values.put(Messages.TYPE, Message.Type.chat.toString());
	values.put(Messages.SUBJECT, "SUBJECT");
	values.put(Messages.BODY, body);
	values.put(Messages.THREAD, "");
	values.put(Messages.EXTRAS, "");
	values.put(Messages.IS_RECEIVE, true);
	values.put(Messages.DATE_RECEIVE, dateFormat.format(new Date()));
	values.put(Messages.DATE_READ, dateFormat.format(new Date()));
	mContext.getContentResolver().insert(Messages.CONTENT_URI, values);

    }

    /**
     * Listen on some Intent broadcast, ScreenOn and ScreenOff.
     */
    private class BeemServiceBroadcastReceiver extends BroadcastReceiver {

	private String mOldStatus;
	private int mOldMode;

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

	@Override
	public void onReceive(final Context context, final Intent intent) {
	    //	    String intentAction = intent.getAction();
	    //	    if (intentAction.equals(Intent.ACTION_SCREEN_OFF)) {
	    //		for (Map.Entry<String, XmppConnectionAdapter> item : mConnection.entrySet()) {
	    //		    XmppConnectionAdapter connection = item.getValue();
	    //		    mOldMode = connection.getPreviousMode();
	    //		    mOldStatus = connection.getPreviousStatus();
	    //		    if (connection.isAuthentificated())
	    //			connection.changeStatus(Status.CONTACT_STATUS_AWAY, mBeemConnection.get(item.getKey())
	    //			    .getSettings().getString("settings_away_message", "Away"));
	    //		}
	    //	    } else if (intentAction.equals(Intent.ACTION_SCREEN_ON)) {
	    //		for (XmppConnectionAdapter connection : mConnection.values()) {
	    //		    if (connection.isAuthentificated())
	    //			connection.changeStatus(mOldMode, mOldStatus);
	    //		}
	    //	    }
	}
    }

    public void syncRoster() {
	BeemSync sync = new BeemSync(mContext);
	sync.manageRoster(mXMPPConnection.getRoster(), mInfoConnection.getJid());
	sync.syncMUC(mXMPPConnection, mInfoConnection.getJid());
    }

    private void setErrorMessage(XMPPException e, String errorMsg) {
	Log.e(TAG, errorMsg, e);
	if (e.getXMPPError() != null) {
	    String condition = e.getXMPPError().getCondition();
	    String message = e.getXMPPError().getMessage();
	    mErrorMsg = condition + " " + message;
	} else if (e.getMessage() != null) {
	    mErrorMsg = e.getMessage();
	} else {
	    mErrorMsg = e.toString();
	}
    }

    public String getErrorMessage() {
	return mErrorMsg;
    }

    //TODO: useless here, delete from this class
    public void editSharedPreferences(String key, String value) {
	SharedPreferences settings = mContext.getSharedPreferences(mInfoConnection.getJid(), Context.MODE_PRIVATE);
	SharedPreferences.Editor prefEditor = settings.edit();
	prefEditor.putString(key, value);
	prefEditor.commit();
    }

    //TODO: useless here, delete from this class
    public void editSharedPreferences(String key, int value) {
	SharedPreferences settings = mContext.getSharedPreferences(mInfoConnection.getJid(), Context.MODE_PRIVATE);
	SharedPreferences.Editor prefEditor = settings.edit();
	prefEditor.putInt(key, value);
	prefEditor.commit();
    }

    public boolean addContact(String jid) {

	Roster r = mXMPPConnection.getRoster();
	if (r == null) {
	    Log.w(TAG, "Has not logged in yet : " + jid);
	    mErrorMsg = "Has not logged in yet : " + jid;
	    return false;
	}
	try {
	    r.createEntry(jid, jid, null);
	} catch (XMPPException e) {
	    setErrorMessage(e, "Problem during add contact");
	    return false;
	}
	return true;
    }

    public boolean mucJoin(String mucjid) {
	MultiUserChat muc = new MultiUserChat(mXMPPConnection, mucjid);

	DiscussionHistory history = new DiscussionHistory();
	history.setMaxStanzas(5);
	Cursor c = mContext.getContentResolver().query(MUCs.CONTENT_URI, new String[] { MUCs.NICKNAME, MUCs.PASSWORD },
	    MUCs.ACCOUNT_NAME + "=? AND " + MUCs.ACCOUNT_TYPE + "=? AND " + MUCs.JID + "=?",
	    new String[] { mInfoConnection.getJid(), BeemApplication.BEEM_PACKAGE, mucjid }, null);
	if (c.moveToFirst()) {
	    try {
		muc.join(c.getString(c.getColumnIndex(MUCs.NICKNAME)), c.getString(c.getColumnIndex(MUCs.PASSWORD)),
		    history, SmackConfiguration.getPacketReplyTimeout());
	    } catch (XMPPException e) {
		setErrorMessage(e, "MUC Join Problem");
		return false;
	    }
	    return true;
	} // else TODO: join directly

	return false;

    }

    public void changeStatus(int status, String msg, int priority) {
	Presence pres = new Presence(Presence.Type.available);
	//Change status
	Presence.Mode mode = Status.getPresenceModeFromStatus(status);
	int m;
	if (mode != null) {
	    m = status;
	} else {
	    m = mPreviousMode;
	}
	pres.setMode(Status.getPresenceModeFromStatus(m));
	mPreviousMode = m;
	// Change status message
	String smode;
	if (msg != null)
	    smode = msg;
	else
	    smode = mPreviousStatus;
	pres.setStatus(smode);
	mPreviousStatus = smode;
	// Change priority
	int p = priority;
	if (priority < SMACK_PRIORITY_MIN)
	    p = SMACK_PRIORITY_MIN;
	if (priority > SMACK_PRIORITY_MAX)
	    p = SMACK_PRIORITY_MAX;
	mPreviousPriority = p;
	pres.setPriority(p);
	mXMPPConnection.sendPacket(pres);
	editSharedPreferences(BeemApplication.STATUS_KEY, status);
	editSharedPreferences(BeemApplication.STATUS_TEXT_KEY, msg);
    }

    public boolean mucSendMsg(String jid, String msg) {
	MultiUserChat mucmsg = new MultiUserChat(mXMPPConnection, jid);
	try {
	    mucmsg.sendMessage(msg);
	} catch (XMPPException e) {
	    setErrorMessage(e, "MUC Error send message");
	    return false;
	}
	return true;
    }

    public void mucAdd(String jid, Bundle extra) {
	String mucName = extra.getString(BeemApplication.MUC_NAME);
	String mucNickname = extra.getString(BeemApplication.MUC_NICKNAME);
	String mucPassword = extra.getString(BeemApplication.MUC_PASSWORD);
	boolean mucAutojoin = extra.getBoolean(BeemApplication.MUC_AUTOJOIN);
	BookmarkManager bm;
	try {
	    bm = BookmarkManager.getBookmarkManager(mXMPPConnection);
	    bm.addBookmarkedConference(mucName, jid, mucAutojoin, mucNickname, mucPassword);
	} catch (XMPPException e) {
	    Log.e(TAG, "Error add muc", e);
	}
    }

    private class BeemPacketFilter implements PacketFilter {

	@Override
	public boolean accept(Packet packet) {
	    return true;
	}

    }
}
