package com.beem.project.beem;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.Roster.SubscriptionMode;
import org.jivesoftware.smack.filter.PacketFilter;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.proxy.ProxyInfo;
import org.jivesoftware.smack.proxy.ProxyInfo.ProxyType;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.ArrayAdapter;

import com.beem.project.beem.service.XmppConnectionAdapter;
import com.beem.project.beem.service.XmppFacade;
import com.beem.project.beem.service.aidl.IBeemConnectionListener;
import com.beem.project.beem.service.aidl.IXmppFacade;
import com.beem.project.beem.ui.Subscription;

/**
 * This class is for the Beem service. The connection to the xmpp server will be made asynchronously
 * when the service will start.
 * 
 * @author darisk
 */
public class BeemService extends Service {

    /**
     * The id to use for status notification.
     */
    public static final int         NOTIFICATION_STATUS_ID = 100;

    private NotificationManager     mNotificationManager;
    private XmppConnectionAdapter   mConnection;
    private SharedPreferences       mSettings;
    private String                  mLogin;
    private String                  mPassword;
    private String                  mHost;
    private String                  mService;
    private int                     mPort;
    private ConnectionConfiguration mConnectionConfiguration;
    private ProxyInfo               mProxyInfo;
    private boolean                 mUseProxy;
    private IXmppFacade.Stub        mBind;

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

    /**
     * Close the connection to the xmpp server.
     */
    private void closeConnection() {
	if (mConnection != null)
	    mConnection.disconnect();
    }

    /**
     * Initialise la configuration de la connexion.
     */
    private void initConnectionConfig() {
	// TODO mettre a false par defaut et remplacer les valeurs par defaut
	mUseProxy = mSettings.getBoolean(getString(R.string.settings_key_proxy_use), false);
	if (mUseProxy) {
	    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.proxy_types,
		    android.R.layout.simple_spinner_item);
	    int pos = mSettings.getInt(getString(R.string.settings_key_proxy_type), 0);
	    String stype = adapter.getItem(pos).toString();
	    String phost = mSettings.getString(getString(R.string.settings_key_proxy_server), "");
	    String puser = mSettings.getString(getString(R.string.settings_key_proxy_username), "");
	    String ppass = mSettings.getString(getString(R.string.settings_key_proxy_password), "");
	    int pport = Integer.parseInt(mSettings.getString(getString(R.string.settings_key_proxy_port), "1080"));
	    ProxyInfo.ProxyType type = ProxyType.valueOf(stype);
	    mProxyInfo = new ProxyInfo(type, phost, pport, puser, ppass);
	    if (mService != null)
		mConnectionConfiguration = new ConnectionConfiguration(mHost, mPort, mService, mProxyInfo);
	    else
		mConnectionConfiguration = new ConnectionConfiguration(mHost, mPort, mProxyInfo);
	} else {
	    if (mService != null)
		mConnectionConfiguration = new ConnectionConfiguration(mHost, mPort, mService);
	    else
		mConnectionConfiguration = new ConnectionConfiguration(mHost, mPort);
	}
	mConnectionConfiguration.setDebuggerEnabled(false);
	mConnectionConfiguration.setSendPresence(true);
    }

    private void initRosterRequestListener() {
	Roster.setDefaultSubscriptionMode(SubscriptionMode.manual);
	final XMPPConnection con = mConnection.getAdaptee();
	try {
	    // l'ajout d'un packet listener ne peut etre effectuer que lorsqu'on est connecte au
	    // serveur
	    mConnection.addConnectionListener(new IBeemConnectionListener.Stub() {

		@Override
		public void connectionClosed() throws RemoteException {
		    Log.i("BeemService", "connectionClosed()");

		}

		@Override
		public void connectionClosedOnError() throws RemoteException {
		    Log.i("BeemService", "connectionClosedOnError()");

		}

		@Override
		public void connectionFailed(String errorMsg) throws RemoteException {
		    Log.i("BeemService", "connectionFailed()");

		}

		@Override
		public void onConnect() throws RemoteException {

		    PacketFilter filter = new PacketFilter() {

			@Override
			public boolean accept(Packet packet) {
			    if (packet instanceof Presence) {
				Presence pres = (Presence) packet;
				if (pres.getType() == Presence.Type.subscribe)
				    return true;
			    }
			    return false;
			}
		    };
		    con.addPacketListener(new PacketListener() {

			@Override
			public void processPacket(Packet packet) {
			    String from = packet.getFrom();
			    Notification notif = new Notification(com.beem.project.beem.R.drawable.signal,
				    "Demande d'ajout", System.currentTimeMillis());
			    notif.defaults = Notification.DEFAULT_ALL;
			    notif.flags = Notification.FLAG_AUTO_CANCEL;
			    Intent intent = new Intent(BeemService.this, Subscription.class);
			    intent.putExtra("from", from);
			    notif
				    .setLatestEventInfo(BeemService.this, from, "demande d'ajout de " + from,
				            PendingIntent.getActivity(BeemService.this, 0, intent,
				                    PendingIntent.FLAG_ONE_SHOT));
			    int id = packet.hashCode();
			    sendNotification(id, notif);
			}
		    }, filter);
		    Log.i("BeemService", "onConnect()");
		}

		@Override
		public void reconnectingIn(int seconds) throws RemoteException {
		    Log.i("BeemService", "reconnectingIn()");

		}

		@Override
		public void reconnectionFailed() throws RemoteException {
		    Log.i("BeemService", "reconnectionFailed()");

		}

		@Override
		public void reconnectionSuccessful() throws RemoteException {
		    Log.i("BeemService", "reconnectionSuccessful()");
		    PacketFilter filter = new PacketFilter() {

			@Override
			public boolean accept(Packet packet) {
			    if (packet instanceof Presence) {
				Presence pres = (Presence) packet;
				if (pres.getType() == Presence.Type.subscribe)
				    return true;
			    }
			    return false;
			}
		    };
		    con.addPacketListener(new PacketListener() {

			@Override
			public void processPacket(Packet packet) {
			    // TODO Auto-generated method stub
			    Log.d("TEST", "Une demande recue");
			}
		    }, filter);
		}
	    });
	} catch (RemoteException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IBinder onBind(Intent intent) {
	return mBind;
	// to forbid a client to bind
	// return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onCreate() {

	super.onCreate();
	mSettings = getSharedPreferences(getString(R.string.settings_filename), MODE_PRIVATE);
	mLogin = mSettings.getString(getString(R.string.settings_key_account_username), "");
	mPassword = mSettings.getString(getString(R.string.settings_key_account_password), "");
	mHost = mSettings.getString(getString(R.string.settings_key_xmpp_server), "");
	mPort = Integer.parseInt(mSettings.getString(getString(R.string.settings_key_xmpp_port), "5222"));

	if (mHost.equals("talk.google.com"))
	    mService = "gmail.com";
	else
	    mService = null;
	initConnectionConfig();
	mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
	mConnection = new XmppConnectionAdapter(mConnectionConfiguration, mLogin, mPassword, this);
	initRosterRequestListener();
	mBind = new XmppFacade(mConnection, this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onDestroy() {
	closeConnection();
	mNotificationManager.cancel(NOTIFICATION_STATUS_ID);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onStart(Intent intent, int startId) {
	try {
	    mConnection.connectAsync();
	} catch (RemoteException e) {
	    // TODO Auto-generated catch block
	    e.printStackTrace();
	}
    }

    /**
     * Show a notification.
     * 
     * @param id
     *            the id of the notification.
     * @param notif
     *            the notification to show
     */
    public void sendNotification(int id, Notification notif) {
	mNotificationManager.notify(id, notif);
    }

}
