package com.vaguehope.onosendai.ui.pref;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

import org.json.JSONException;

import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import twitter4j.conf.Configuration;
import twitter4j.conf.ConfigurationBuilder;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;

import com.vaguehope.onosendai.config.Account;
import com.vaguehope.onosendai.config.AccountProvider;
import com.vaguehope.onosendai.provider.twitter.TwitterOauth;
import com.vaguehope.onosendai.util.DialogHelper;
import com.vaguehope.onosendai.util.LogWrapper;
import com.vaguehope.onosendai.util.Result;

public class TwitterOauthWizard {

	private static final LogWrapper LOG = new LogWrapper("TOW");

	private final Context context;
	private final TwitterOauthHelper helperCallback;

	private Configuration twitterConfiguration;
	private RequestToken requestToken;

	public interface TwitterOauthHelper {
		void deligateStartActivityForResult (Intent intent, int requestCode);
	}

	public interface TwitterOauthComplete {
		String getAccountId ();
		void onAccount (Account account, String screenName) throws JSONException;
	}

	private final AtomicInteger requestCodeCounter = new AtomicInteger(100);
	private final Map<Integer, TwitterOauthComplete> completeCallbacks = new ConcurrentHashMap<Integer, TwitterOauthWizard.TwitterOauthComplete>();
	private final ConcurrentMap<Integer, Twitter> twitters = new ConcurrentHashMap<Integer, Twitter>();

	public TwitterOauthWizard (final Context context, final TwitterOauthHelper helperCallback) {
		this.context = context;
		this.helperCallback = helperCallback;
	}

	public void start (final TwitterOauthComplete completeCallback) {
		final int requestCode = Integer.valueOf(this.requestCodeCounter.incrementAndGet());
		this.completeCallbacks.put(requestCode, completeCallback);
		new TwitterOauthInitTask(this, requestCode).execute();
	}

	protected static LogWrapper getLog () {
		return LOG;
	}

	protected Context getContext () {
		return this.context;
	}

	public TwitterOauthHelper getHelperCallback () {
		return this.helperCallback;
	}

	private Configuration getTwitterConfiguration () {
		if (this.twitterConfiguration == null) {
			final ConfigurationBuilder builder = new ConfigurationBuilder();
			builder.setOAuthConsumerKey(TwitterOauth.getConsumerKey());
			builder.setOAuthConsumerSecret(TwitterOauth.getConsumerSecret());
			this.twitterConfiguration = builder.build();
		}
		return this.twitterConfiguration;
	}

	private Twitter makeTwitter () {
		final TwitterFactory factory = new TwitterFactory(getTwitterConfiguration());
		return factory.getInstance();
	}

	protected Twitter getTwitter (final int requestCode) {
		final Twitter existingT = this.twitters.get(Integer.valueOf(requestCode));
		if (existingT != null) return existingT;
		final Twitter newT = makeTwitter();
		this.twitters.putIfAbsent(requestCode, newT);
		return this.twitters.get(Integer.valueOf(requestCode));
	}

	protected void stashRequestToken (final RequestToken token) {
		if (this.requestToken != null) throw new IllegalStateException("Request token alrady stashed.");
		if (token == null) throw new IllegalStateException("Can not stash a null token.");
		this.requestToken = token;
	}

	protected RequestToken unstashRequestToken () {
		if (this.requestToken == null) throw new IllegalStateException("Request token has not been stashed.");
		final RequestToken token = this.requestToken;
		this.requestToken = null;
		return token;
	}

	private static class TwitterOauthInitTask extends AsyncTask<Void, Void, Result<RequestToken>> {

		private final TwitterOauthWizard host;
		private final Integer requestCode;
		private ProgressDialog dialog;

		public TwitterOauthInitTask (final TwitterOauthWizard host, final Integer requestCode) {
			this.host = host;
			this.requestCode = requestCode;
		}

		@Override
		protected void onPreExecute () {
			this.dialog = ProgressDialog.show(this.host.getContext(), "Onosendai", "Starting Oauth...", true); //ES
		}

		@Override
		protected Result<RequestToken> doInBackground (final Void... params) {
			try {
				return new Result<RequestToken>(this.host.getTwitter(this.requestCode).getOAuthRequestToken(TwitterOauth.CALLBACK_URL));
			}
			catch (final Exception e) { // NOSONAR report all errors to user.
				return new Result<RequestToken>(e);
			}
		}

		@Override
		protected void onPostExecute (final Result<RequestToken> result) {
			this.dialog.dismiss();
			if (result.isSuccess()) {
				final Intent intent = new Intent(this.host.getContext(), TwitterLoginActivity.class);
				final RequestToken requtestToken = result.getData();
				this.host.stashRequestToken(requtestToken);
				intent.putExtra(TwitterOauth.IEXTRA_AUTH_URL, requtestToken.getAuthorizationURL());
				this.host.getHelperCallback().deligateStartActivityForResult(intent, this.requestCode);
			}
			else {
				getLog().e("Failed to init OAuth.", result.getE());
				DialogHelper.alert(this.host.getContext(), result.getE());
			}
		}

	}

	public void onActivityResult (final int requestCode, final int resultCode, final Intent intent) {
		LOG.d("onActivityResult(%d, %d, %s)", requestCode, resultCode, intent);
		final TwitterOauthComplete completeCallback = this.completeCallbacks.get(Integer.valueOf(requestCode));
		if (completeCallback != null) {
			if (resultCode == Activity.RESULT_OK) {
				final String oauthVerifier = intent.getExtras().getString(TwitterOauth.IEXTRA_OAUTH_VERIFIER);
				new TwitterOauthPostTask(this, requestCode, oauthVerifier, completeCallback).execute();
			}
			else if (resultCode == Activity.RESULT_CANCELED) {
				DialogHelper.alert(getContext(), "Twitter auth canceled."); //ES
			}
		}
	}

	private static class TwitterOauthPostTask extends AsyncTask<Void, Void, Result<AccessToken>> {

		private final TwitterOauthWizard host;
		private final int requestCode;
		private final String oauthVerifier;
		private final TwitterOauthComplete completeCallback;
		private ProgressDialog dialog;

		public TwitterOauthPostTask (final TwitterOauthWizard host, final int requestCode, final String oauthVerifier, final TwitterOauthComplete completeCallback) {
			this.host = host;
			this.requestCode = requestCode;
			this.oauthVerifier = oauthVerifier;
			this.completeCallback = completeCallback;
		}

		@Override
		protected void onPreExecute () {
			this.dialog = ProgressDialog.show(this.host.getContext(), "Onosendai", "Completing Oauth...", true); //ES
		}

		@Override
		protected Result<AccessToken> doInBackground (final Void... params) {
			try {
				final RequestToken token = this.host.unstashRequestToken();
				return new Result<AccessToken>(this.host.getTwitter(this.requestCode).getOAuthAccessToken(token, this.oauthVerifier));
			}
			catch (final Exception e) { // NOSONAR report all errors to user.
				return new Result<AccessToken>(e);
			}
		}

		@Override
		protected void onPostExecute (final Result<AccessToken> result) {
			this.dialog.dismiss();
			if (result.isSuccess()) {
				this.host.onGotTwitterAccessToken(result.getData(), this.completeCallback);
			}
			else {
				getLog().e("Failed to complete OAuth.", result.getE());
				DialogHelper.alert(this.host.getContext(), result.getE());
			}
		}

	}

	protected void onGotTwitterAccessToken (final AccessToken accessToken, final TwitterOauthComplete completeCallback) {
		try {
			LOG.i("Account authorised %s.", accessToken.getScreenName());
			final Account account = new Account(completeCallback.getAccountId(), accessToken.getScreenName(), AccountProvider.TWITTER,
					getTwitterConfiguration().getOAuthConsumerKey(), getTwitterConfiguration().getOAuthConsumerSecret(),
					accessToken.getToken(), accessToken.getTokenSecret());
			completeCallback.onAccount(account, accessToken.getScreenName());
		}
		catch (final Exception e) { // NOSONAR want to show any errors to the user.
			LOG.e("Failed to write new Twitter account.", e);
			DialogHelper.alert(getContext(), e);
		}
	}

}