package fr.unix_experience.owncloud_sms.activities;

/*
 *  Copyright (c) 2014-2015, Loic Blot <loic.blot@unix-experience.fr>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program 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 Affero General Public License for more details.
 *  
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import android.accounts.Account;
import android.accounts.AccountManager;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.OwnCloudClientFactory;
import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;

import fr.unix_experience.owncloud_sms.R;
import fr.unix_experience.owncloud_sms.authenticators.OwnCloudAuthenticator;
import fr.unix_experience.owncloud_sms.enums.LoginReturnCode;

/**
 * A login screen that offers login via email/password.
 */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class LoginActivity extends Activity {
	/**
	 * Keep track of the login task to ensure we can cancel it if requested.
	 */
	private UserLoginTask mAuthTask = null;

	// UI references.
	private Spinner _protocolView;
	private EditText _loginView;
	private EditText _passwordView;
	private EditText _serverView;
	private View mProgressView;
	private View mLoginFormView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_login);

		// Set up the login form.
		_protocolView = (Spinner) findViewById(R.id.oc_protocol);
		_serverView = (EditText) findViewById(R.id.oc_server);
		_loginView = (EditText) findViewById(R.id.oc_login);

		_passwordView = (EditText) findViewById(R.id.oc_password);
		_passwordView
				.setOnEditorActionListener(new TextView.OnEditorActionListener() {
					@Override
					public boolean onEditorAction(TextView textView, int id,
							KeyEvent keyEvent) {
						if (id == R.id.oc_login || id == EditorInfo.IME_NULL) {
							attemptLogin();
							return true;
						}
						return false;
					}
				});

		Button _signInButton = (Button) findViewById(R.id.oc_signin_button);
		_signInButton.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View view) {
				attemptLogin();
			}
		});

		mLoginFormView = findViewById(R.id.login_form);
		mProgressView = findViewById(R.id.login_progress);
	}

	/**
	 * Attempts to sign in or register the account specified by the login form.
	 * If there are form errors (invalid email, missing fields, etc.), the
	 * errors are presented and no actual login attempt is made.
	 */
	public void attemptLogin() {
		if (mAuthTask != null) {
			return;
		}

		// Reset errors.
		_loginView.setError(null);
		_passwordView.setError(null);

		// Store values at the time of the login attempt.
		String protocol = _protocolView.getSelectedItem().toString();
		String login = _loginView.getText().toString();
		String password = _passwordView.getText().toString();
		String serverAddr = _serverView.getText().toString();

		boolean cancel = false;
		View focusView = null;
		
		// Check for a valid server address.
		if (TextUtils.isEmpty(protocol)) {
			cancel = true;
		}
		
		// Check for a valid server address.
		if (TextUtils.isEmpty(serverAddr)) {
			_serverView.setError(getString(R.string.error_field_required));
			focusView = _loginView;
			cancel = true;
		}

		// Check for a valid login address.
		if (TextUtils.isEmpty(login)) {
			_loginView.setError(getString(R.string.error_field_required));
			focusView = _loginView;
			cancel = true;
		}
				
		// Check for a valid password
		if (TextUtils.isEmpty(password)) {
			_passwordView.setError(getString(R.string.error_field_required));
			focusView = _passwordView;
			cancel = true;
		}
		
		if (!isPasswordValid(password)) {
			_passwordView.setError(getString(R.string.error_invalid_password));
			focusView = _passwordView;
			cancel = true;
		}

		if (cancel) {
			// There was an error; don't attempt login and focus the first
			// form field with an error.
			focusView.requestFocus();
		} else {
			// Show a progress spinner, and kick off a background task to
			// perform the user login attempt.
			showProgress(true);
			String serverURL = new String(protocol + serverAddr);
			mAuthTask = new UserLoginTask(serverURL, login, password);
			mAuthTask.execute((Void) null);
		}
	}

	private boolean isPasswordValid(String password) {
		// TODO: Replace this with your own logic
		return password.length() > 4;
	}

	/**
	 * Shows the progress UI and hides the login form.
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
	public void showProgress(final boolean show) {
		// On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
		// for very easy animations. If available, use these APIs to fade-in
		// the progress spinner.
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
			int shortAnimTime = getResources().getInteger(
					android.R.integer.config_shortAnimTime);

			mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
			mLoginFormView.animate().setDuration(shortAnimTime)
					.alpha(show ? 0 : 1)
					.setListener(new AnimatorListenerAdapter() {
						@Override
						public void onAnimationEnd(Animator animation) {
							mLoginFormView.setVisibility(show ? View.GONE
									: View.VISIBLE);
						}
					});

			mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
			mProgressView.animate().setDuration(shortAnimTime)
					.alpha(show ? 1 : 0)
					.setListener(new AnimatorListenerAdapter() {
						@Override
						public void onAnimationEnd(Animator animation) {
							mProgressView.setVisibility(show ? View.VISIBLE
									: View.GONE);
						}
					});
		} else {
			// The ViewPropertyAnimator APIs are not available, so simply show
			// and hide the relevant UI components.
			mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
			mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
		}
	}

	/**
	 * Represents an asynchronous login/registration task used to authenticate
	 * the user.
	 */
	public class UserLoginTask extends AsyncTask<Void, Void, Boolean> { 

		UserLoginTask(String serverURI, String login, String password) {
			_serverURI = Uri.parse(serverURI);
			_login = login;
			_password = password;
		}

		@Override
		protected Boolean doInBackground(Void... params) {
			// Create client object to perform remote operations
			OwnCloudClient ocClient = OwnCloudClientFactory.createOwnCloudClient(
				_serverURI, getBaseContext(), 
				// Activity or Service context
				true
			);
			
			// Set basic credentials
			ocClient.setCredentials(
				OwnCloudCredentialsFactory.newBasicCredentials(_login, _password)
			);
			
			// Send an authentication test to ownCloud
			OwnCloudAuthenticator at = new OwnCloudAuthenticator(getBaseContext());
			at.setClient(ocClient);
			
			_returnCode = at.testCredentials();
			
			return (_returnCode == LoginReturnCode.OK);
		}

		@Override
		protected void onPostExecute(final Boolean success) {
			mAuthTask = null;
			showProgress(false);

			if (success) {
				String accountType = getIntent().getStringExtra(PARAM_AUTHTOKEN_TYPE);
				if (accountType == null) {  
		            accountType = getString(R.string.account_type);  
		        }
				
				// Generate a label
				String accountLabel = _login + "@" + _serverURI.getHost();
				
				// We create the account
				final Account account = new Account(accountLabel, accountType);
				Bundle accountBundle = new Bundle();
				accountBundle.putString("ocLogin", _login);
				accountBundle.putString("ocURI", _serverURI.toString());
				
				// And we push it to Android
				AccountManager accMgr = AccountManager.get(getApplicationContext());
				accMgr.addAccountExplicitly(account, _password, accountBundle);  
				
				// Set sync options
				ContentResolver.setSyncAutomatically(account, getString(R.string.account_authority), true);
				
				Bundle b = new Bundle();
				b.putInt("synctype", 1);
				
				ContentResolver.addPeriodicSync(account, getString(R.string.account_authority), b, 15 * 60);
				// Then it's finished
				finish();
				
				// Start sync settings, we have finished to configure account
				Intent settingsIntent = new Intent(Settings.ACTION_SYNC_SETTINGS);
				settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				getApplicationContext().startActivity(settingsIntent);
			} else {
				switch (_returnCode) {
					case INVALID_ADDR:
						_serverView.setError(getString(R.string.error_invalid_server_address));
						_serverView.requestFocus();
						break;
					case HTTP_CONN_FAILED:
						_serverView.setError(getString(R.string.error_http_connection_failed));
						_serverView.requestFocus();
						break;
					case CONN_FAILED:
						_serverView.setError(getString(R.string.error_connection_failed));
						_serverView.requestFocus();
						break;
					case INVALID_LOGIN:
						_passwordView.setError(getString(R.string.error_invalid_login));
						_passwordView.requestFocus();
						break;
					case UNKNOWN_ERROR:
						_serverView.setError("UNK");
						_serverView.requestFocus();
						break;
				default:
					break;
				}
				
			}
		}

		@Override
		protected void onCancelled() {
			mAuthTask = null;
			showProgress(false);
		}
		
		private final Uri _serverURI;
		private final String _login;
		private final String _password;
		private LoginReturnCode _returnCode;
		
		public static final String PARAM_AUTHTOKEN_TYPE = "auth.token";  
	    public static final String PARAM_CREATE = "create"; 
	}
}
