package com.beem.project.beem.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

import com.beem.project.beem.R;
import com.beem.project.beem.service.aidl.IBeemConnectionListener;
import com.beem.project.beem.service.aidl.IXmppConnection;
import com.beem.project.beem.service.aidl.IXmppFacade;
import com.beem.project.beem.utils.Status;

/**
 * This class represents an activity which allows the user to connect to an XMPP server with his username/password.
 * @author Jean-Manuel Da Silva <dasilvj at beem-project dot com>
 */
public class Login extends Activity {

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

    private final Handler mConnectionHandler = new Handler();
    private final ConnectionRunnable mConnectionRunnable = new ConnectionRunnable();
    private ProgressDialog mProgressDialog;

    private boolean mIsConnectedService;
    private final ServiceConnection mServConn = new BeemServiceConnection();
    private IXmppFacade mXmppFacade;

    private SharedPreferences mSettings;
    private boolean mIsConfigured;
    private int mRetry;
    private IXmppConnection mXmppConnection;

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

    /**
     * Create an about "BEEM" dialog.
     */
    public void createAboutDialog() {
	AlertDialog.Builder builder = new AlertDialog.Builder(this);
	builder.setTitle(R.string.login_about_title).setMessage(R.string.login_about_msg).setCancelable(false);
	builder.setNeutralButton(R.string.login_about_button, new DialogInterface.OnClickListener() {

	    public void onClick(DialogInterface dialog, int whichButton) {
		dialog.cancel();
	    }
	});
	AlertDialog aboutDialog = builder.create();
	aboutDialog.show();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	Log.d(TAG, "BEGIN onCreate.");
	mSettings = PreferenceManager.getDefaultSharedPreferences(this);
	setContentView(R.layout.login);
	mProgressDialog = new ProgressDialog(this);
	Log.d(TAG, "END onCreate.");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onStop() {
	super.onStop();
	Log.d(TAG, "BEGIN onStop.");
	if (mIsConfigured && (mIsConnectedService || mXmppFacade != null)) {
	    boolean isConnected = false;
	    if (mXmppConnection != null) {
		try {
		    isConnected = mXmppConnection.isAuthentificated();
		} catch (RemoteException e) {
		    e.printStackTrace();
		}
	    }
	    unbindService(mServConn);
	    if (!isConnected) {
		stopService(SERVICE_INTENT);
	    }
	    mXmppFacade = null;
	}
	Log.d(TAG, "END onStop.");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onPause() {
	super.onPause();
	Log.d(TAG, "BEGIN onPause.");
	mConnectionHandler.removeCallbacks(mConnectionRunnable);
	mProgressDialog.dismiss();
	Log.d(TAG, "END onPause.");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onResume() {
	super.onResume();
	Log.d(TAG, "BEGIN onResume.");
	Log.d(TAG, "END onResume.");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void onStart() {
	super.onStart();
	Log.d(TAG, "BEGIN onStart.");
	mIsConfigured = mSettings.getBoolean("PreferenceIsConfigured", false);
	if (mIsConfigured && !mIsConnectedService) {
	    try {
		Thread.sleep(1000); // HACK: Pour éviter de rebinder un service
		// pas encore arreter.
	    } catch (InterruptedException e) {
		Log.e(TAG, e.getMessage());
	    }
	    bindService(Login.SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
	    mIsConnectedService = true;
	}
	Log.d(TAG, "END onStart.");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	if (requestCode == REQUEST_CODE) {
	    if (resultCode == RESULT_OK) {
		stopService(SERVICE_INTENT);
	    }
	}
    }

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

    /**
     * Callback for menu item selected.
     * @param item the item selected
     * @return true on success, false otherwise
     */
    @Override
    public final boolean onOptionsItemSelected(MenuItem item) {
	switch (item.getItemId()) {
	    case R.id.login_menu_settings:
		startActivity(new Intent(Login.this, Settings.class));
		return true;
	    case R.id.login_menu_about:
		createAboutDialog();
		return true;
	    default:
		return false;
	}
    }

    /**
     * Connection runnable.
     * @author nikita
     */
    private class ConnectionRunnable implements Runnable {

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void run() {
	    if (mRetry++ > 3) {
		Toast.makeText(Login.this, R.string.login_max_retry, Toast.LENGTH_LONG).show();
		mConnectionHandler.removeCallbacks(mConnectionRunnable);
	    } else {
		mIsConfigured = mSettings.getBoolean("PreferenceIsConfigured", false);
		if (mIsConfigured) {
		    bindService(Login.SERVICE_INTENT, mServConn, BIND_AUTO_CREATE);
		}
	    }

	}
    }

    /**
     * Listener use to check the state of the connection with the server.
     */
    private class BeemConnectionListener extends IBeemConnectionListener.Stub {

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void connectionClosed() throws RemoteException {
	    Log.d(TAG, "BEGIN connectionClosed.");
	    if (mXmppFacade != null) {
		launchReconnectionHandler();
		mXmppFacade = null;
	    }
	    Log.d(TAG, "END connectionClosed.");
	}

	@Override
	public void connectionClosedOnError() throws RemoteException {
	    Log.d(TAG, "BEGIN connectionClosedOnError.");
	    if (mXmppFacade != null) {
		launchReconnectionHandler();
		mXmppFacade = null;
	    }
	    Log.d(TAG, "END connectionClosedOnError.");
	}

	@Override
	public void connectionFailed(String errorMsg) throws RemoteException {
	    Log.d(TAG, "BEGIN connectionFailed.");
	    if (mXmppFacade != null) {
		Login.this.unbindService(mServConn);
		Login.this.stopService(SERVICE_INTENT);
		mIsConnectedService = false;
		mXmppFacade = null;
	    }
	    dismissProgressDialog();
	    showToast(errorMsg);
	    launchReconnectionHandler();
	    Log.d(TAG, "END connectionFailed.");
	}

	/**
	 * launch reconnection handler.
	 */
	private void launchReconnectionHandler() {
	    mConnectionHandler.postAtTime(mConnectionRunnable, Integer.parseInt(mSettings.getString(
		"settings_key_reco_delay", "10"))
		* 1000 + SystemClock.uptimeMillis());
	}

	/**
	 * Show an error message with a toast.
	 * @param errorMsg The message to display.
	 */
	private void showToast(final String errorMsg) {
	    mConnectionHandler.post(new Runnable() {
		/**
		 * @{inheritDoc
		 */
		@Override
		public void run() {
		    Toast.makeText(Login.this, errorMsg, Toast.LENGTH_LONG).show();
		    TextView labelError = (TextView) findViewById(R.id.log_as_msg);
		    labelError.setText(getString(R.string.login_error_msg, errorMsg));
		}
	    });
	}

	/**
	 * Dismiss the progress dialog.
	 */
	private void dismissProgressDialog() {
	    mConnectionHandler.post(new Runnable() {

		/**
		 * @{inheritDoc
		 */
		@Override
		public void run() {
		    mProgressDialog.dismiss();
		}
	    });
	}

	@Override
	public void onConnect() throws RemoteException {
	    Log.d(TAG, "BEGIN onConnect.");
	    dismissProgressDialog();
	    mXmppFacade.changeStatus(Status.CONTACT_STATUS_AVAILABLE, null);
	    mRetry = 0;
	    startActivity(new Intent(Login.this, ContactList.class));
	    finish();
	    Log.d(TAG, "END onConnect.");
	}

	@Override
	public void reconnectingIn(int seconds) throws RemoteException {
	}

	@Override
	public void reconnectionFailed() throws RemoteException {
	}

	@Override
	public void reconnectionSuccessful() throws RemoteException {
	}
    }

    /**
     * ServiceConnection use to connect to the Beem Service.
     */
    private class BeemServiceConnection implements ServiceConnection {
	/**
	 * Constructor.
	 */
	public BeemServiceConnection() {
	}

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
	    Log.d(TAG, "BEGIN onServiceConnected.");
	    mXmppFacade = IXmppFacade.Stub.asInterface(service);
	    mIsConnectedService = true;
	    try {
		mXmppConnection = mXmppFacade.createConnection();
		mXmppConnection.addConnectionListener(new BeemConnectionListener());
		if (!mXmppConnection.isAuthentificated()) {
		    mConnectionHandler.post(new Runnable() {
			@Override
			public void run() {
			    mProgressDialog.setMessage(getString(R.string.login_login_progress));
			    mProgressDialog.show();
			}
		    });
		    Login.this.startService(Login.SERVICE_INTENT);
		} else {
		    startActivity(new Intent(Login.this, ContactList.class));
		    finish();
		}
	    } catch (RemoteException e) {
		Log.e(TAG, e.getMessage());
	    }
	    Log.d(TAG, "END onServiceConnected.");
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
	    Log.d(TAG, "BEGIN onServiceDisconnected.");
	    mIsConnectedService = false;
	    mXmppFacade = null;
	    Log.d(TAG, "END onServiceDisconnected.");
	}
    }
}
