package jp.sourceforge.nicoro;

import java.io.UnsupportedEncodingException;
import java.util.Set;

import jp.gr.java_conf.shiseissi.commonlib.ViewUtil;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

public class LoginActivity extends Activity
implements Handler.Callback {
    private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

	public static final String INTENT_NAME_EDIT_LOGIN =
		"jp.sourceforge.nicoro.intent.action.EDIT_LOGIN";

    private static final int MSG_ID_UPDATE_PROGRESS_TEXT = 0;

	private SharedPreferences mSharedPreferences;
	private InputMethodManager mInputMethodManager;

	private TextView mMailAddress;
	private EditText mInputMailAddress;
	private TextView mPassword;
	private EditText mInputPassword;
	private TextView mInstructionsAccount;
	private Button mButtonLogin;
//	private Button mButtonWithoutLogin;
	private Button mButtonHelp;

    private View mProgress;
	private TextView mProgressText;

	private String mUserAgent;
	private String mCookieUserSession;

	private boolean mIsEditLogin;

    private final HandlerWrapper mHandler = new HandlerWrapper(this);

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_ID_UPDATE_PROGRESS_TEXT:
                if (mProgressText != null) {
                    int strId = msg.arg1;
                    if (strId == 0) {
                        String str = (String) msg.obj;
                        mProgressText.setText(str);
                    } else {
                        mProgressText.setText(strId);
                    }
                }
                break;
            default:
                assert false : msg.what;
                break;
        }
        return true;
    }

	private AsyncGetAuthorizeCookie mAsyncGetAuthorizeCookie = null;
	private class AsyncGetAuthorizeCookie extends AsyncTask<Void, Void, NicoroAPIManager.AuthorizeCookie> {
		private HttpPost mHttpRequest;
		private String mTempMail;
		private String mTempPassword;

		@Override
		protected void onPreExecute() {
			try {
				mHttpRequest = NicoroAPIManager.createRequestGetAuthorizeCookie(
						mTempMail, mTempPassword, mUserAgent);
                mHandler.obtainMessage(MSG_ID_UPDATE_PROGRESS_TEXT,
                        R.string.login_progress, 0).sendToTarget();
			} catch (UnsupportedEncodingException e) {
				Log.e(LOG_TAG, e.toString(), e);
				mHttpRequest = null;
			}
		}

		@Override
		protected NicoroAPIManager.AuthorizeCookie doInBackground(Void... params) {
			if (mHttpRequest == null) {
				return null;
			}
			DefaultHttpClient httpClient = Util.createHttpClient();
			try {
			    NicoroAPIManager.AuthorizeCookie cookie = NicoroAPIManager.getAuthorizeCookie(
						httpClient, mHttpRequest);
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, Log.buf().append("getAuthorizeCookie: ")
							.append(cookie)
							.toString());
				}
				return cookie;
			} finally {
			    httpClient.getConnectionManager().shutdown();
			}
		}

		@Override
		protected void onPostExecute(NicoroAPIManager.AuthorizeCookie result) {
			if (result == null || result.cookie.length() == 0) {
				Util.showErrorToast(getApplicationContext(),
						R.string.toast_login_failed);

				// 登録し直し
				showLoginView();
				hideProgressView();
			} else {
				Util.showInfoToast(getApplicationContext(),
						R.string.toast_login_succeeded);

		        SharedPreferences.Editor editor = mSharedPreferences.edit();
		        editor.putString(NicoroConfig.COOKIE_USER_SESSION,
		                NicoroAPIManager.getUserSessionFromCookie(result.cookie));
                editor.putInt(NicoroConfig.AUTHFLAG, result.authflag);
		        NicoroConfig.putMA(editor, mTempMail);
		        NicoroConfig.putPW(editor, mTempPassword);
				editor.commit();

				endLogin();
			}

			mAsyncGetAuthorizeCookie = null;
		}

		public AsyncGetAuthorizeCookie executeWrapper(String mail,
				String password) {
			mTempMail = mail;
			mTempPassword = password;
			return (AsyncGetAuthorizeCookie) execute();
		}

		public void stop() {
		    Util.abortHttpUriRequest(mHttpRequest);
		}
	}

	private AsyncCheckCookieUserSession mAsyncCheckCookieUserSession = null;
	private class AsyncCheckCookieUserSession extends AsyncTask<Void, Void, Integer> {
		private HttpUriRequest mHttpRequest;

		@Override
		protected void onPreExecute() {
			mHttpRequest =
				NicoroAPIManager.createRequestIsCookieUserSessionValid(
						mCookieUserSession, mUserAgent);
            mHandler.obtainMessage(MSG_ID_UPDATE_PROGRESS_TEXT,
                    R.string.login_progress_cookie, 0).sendToTarget();
		}

		@Override
		protected Integer doInBackground(Void... params) {
            DefaultHttpClient httpClient = Util.createHttpClient();
            try {
                int authFlag = NicoroAPIManager.getAuthFlag(mHttpRequest, httpClient);
                if (authFlag == 0) {
                    return 0;
                }
                return authFlag;
            } finally {
                httpClient.getConnectionManager().shutdown();
            }
		}

		@Override
		protected void onPostExecute(Integer result) {
			if (result != 0) {
                SharedPreferences.Editor editor = mSharedPreferences.edit();
                editor.putInt(NicoroConfig.AUTHFLAG, result);
                editor.commit();

				Util.showInfoToast(getApplicationContext(),
						R.string.toast_login_succeeded);

				endLogin();
			} else {
		        SharedPreferences.Editor editor = mSharedPreferences.edit();
		        editor.putString(NicoroConfig.COOKIE_USER_SESSION,
		        		null);
                editor.putInt(NicoroConfig.AUTHFLAG, 0);
		        editor.commit();

		        // 再ログイン
				// 非同期処理に渡すので念のためコピー作成
				String mail = mInputMailAddress.getText().toString();
				String password = mInputPassword.getText().toString();

				mAsyncGetAuthorizeCookie = new AsyncGetAuthorizeCookie();
				mAsyncGetAuthorizeCookie.executeWrapper(mail, password);
			}

			mAsyncCheckCookieUserSession = null;
		}

		public void stop() {
		    Util.abortHttpUriRequest(mHttpRequest);
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		String lastUncaught = NicoroApplication.getInstance(this).processLastUncaughtException();
		if (lastUncaught == null) {
	        onCreateMain();
		} else {
		    // 前回の強制終了情報表示
		    // TODO: LoginActivity以外を直接起動した場合に表示されない、設定でログイン編集呼び出した場合に表示される可能性有り
		    Util.createCloseDialog(this,
		            getString(R.string.dialog_title_last_uncaught_exception),
		            lastUncaught,
		            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    onCreateMain();
                }
		    }, new DialogInterface.OnCancelListener() {
                @Override
                public void onCancel(DialogInterface dialog) {
                    onCreateMain();
                }
		    }).show();
		}
	}

	private void onCreateMain() {
        setContentView(R.layout.login);
        getLoginView();
        getProgressView();
        hideProgressView();

        Intent intent = getIntent();
        mIsEditLogin = (intent != null
                && INTENT_NAME_EDIT_LOGIN.equals(intent.getAction()));

        SharedPreferences sp = Util.getDefaultSharedPreferencesMultiProcess(
                getApplicationContext());
        mSharedPreferences = sp;

        mUserAgent = sp.getString(NicoroConfig.USER_AGENT, null);

        String ma = NicoroConfig.getMA(sp);
        String pw = NicoroConfig.getPW(sp);
        if (ma != null) {
            mInputMailAddress.setText(ma);
        }
        if (pw != null) {
            mInputPassword.setText(pw);
        }

        if (mIsEditLogin) {
            showLoginView();
        } else {
            if (ma == null || pw == null) {
                // ログインView表示
                showLoginView();
            } else {
                // Cookie確認
                mCookieUserSession = NicoroConfig.getCookieUserSession(sp);
                mAsyncCheckCookieUserSession = new AsyncCheckCookieUserSession();
                mAsyncCheckCookieUserSession.execute();

                showProgressView();
            }
        }
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

        if (mAsyncGetAuthorizeCookie != null) {
        	mAsyncGetAuthorizeCookie.cancel(false);
        	mAsyncGetAuthorizeCookie.stop();
        }
        if (mAsyncCheckCookieUserSession != null) {
        	mAsyncCheckCookieUserSession.cancel(false);
        	mAsyncCheckCookieUserSession.stop();
        }
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
	    if (mIsEditLogin) {
	        return super.onCreateOptionsMenu(menu);
	    }
        getMenuInflater().inflate(R.menu.login_menu, menu);
        return true;
	}

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mIsEditLogin) {
            return super.onOptionsItemSelected(item);
        }
        switch (item.getItemId()) {
            case R.id.menu_without_login:
                endLogin();
                return true;
            case R.id.menu_config:
                startActivity(new Intent(getApplicationContext(),
                        NicoroConfig.class));
                return true;
            case R.id.menu_help:
                showHelp();
                return true;
            default:
                assert false : item.getItemId();
                break;
        }
        return false;
    }

	private void getLoginView() {
		if (mMailAddress == null) {
			mMailAddress = ViewUtil.findViewById(this, R.id.mail_address);
		}
		if (mInputMailAddress == null) {
			mInputMailAddress = ViewUtil.findViewById(this, R.id.input_mail_address);
			mInputMailAddress.addTextChangedListener(new SpaceTextWatcher(
			        mInputMailAddress, getString(R.string.error_login_contain_space)));
		}
		if (mPassword == null) {
			mPassword = ViewUtil.findViewById(this, R.id.password);
		}
		if (mInputPassword == null) {
			mInputPassword = ViewUtil.findViewById(this, R.id.input_password);
			mInputPassword.addTextChangedListener(new SpaceTextWatcher(
			        mInputPassword, getString(R.string.error_login_contain_space)));
		}
        if (mInstructionsAccount == null) {
            mInstructionsAccount = ViewUtil.findViewById(this, R.id.instructions_account);
        }
		if (mButtonLogin == null) {
			mButtonLogin = ViewUtil.findViewById(this, R.id.button_login);
			mButtonLogin.setOnClickListener(new LoginButtonListener());
		}
//		if (mButtonWithoutLogin == null) {
//			mButtonWithoutLogin = Util.findViewById(this, R.id.button_without_login);
//			mButtonWithoutLogin.setOnClickListener(new WithoutLoginButtonListener());
//		}
		if (mButtonHelp == null) {
			mButtonHelp = ViewUtil.findViewById(this, R.id.button_help);
			mButtonHelp.setOnClickListener(new HelpButtonListener());
		}
	}

	private void getProgressView() {
		if (mProgress == null) {
			mProgress = ViewUtil.findViewById(this, R.id.progress);
		}
		if (mProgressText == null) {
			mProgressText = ViewUtil.findViewById(this, R.id.progress_text);
		}
	}

	private void showLoginView() {
		mMailAddress.setVisibility(View.VISIBLE);
		mInputMailAddress.setVisibility(View.VISIBLE);
		mPassword.setVisibility(View.VISIBLE);
		mInputPassword.setVisibility(View.VISIBLE);
		mInstructionsAccount.setVisibility(View.VISIBLE);
		mButtonLogin.setVisibility(View.VISIBLE);
		if (!mIsEditLogin) {
//			mButtonWithoutLogin.setVisibility(View.VISIBLE);
			mButtonHelp.setVisibility(View.VISIBLE);
		}
	}

	private void hideLoginView() {
		mMailAddress.setVisibility(View.INVISIBLE);
		mInputMailAddress.setVisibility(View.INVISIBLE);
		mPassword.setVisibility(View.INVISIBLE);
		mInputPassword.setVisibility(View.INVISIBLE);
        mInstructionsAccount.setVisibility(View.INVISIBLE);
		mButtonLogin.setVisibility(View.INVISIBLE);
//		mButtonWithoutLogin.setVisibility(View.INVISIBLE);
		mButtonHelp.setVisibility(View.INVISIBLE);
	}

	private void showProgressView() {
		mProgress.setVisibility(View.VISIBLE);
	}

	private void hideProgressView() {
		mProgress.setVisibility(View.INVISIBLE);
	}

	private void endLogin() {
		if (mIsEditLogin) {

		} else {
			startMainActivity(getIntent());
		}
		finish();
	}

	private void startMainActivity(Intent srcIntent) {
	    String srcAction = srcIntent.getAction();
	    Intent launchIntent;
	    if (Intent.ACTION_VIEW.equals(srcAction)) {
	        launchIntent = new Intent(
	                srcAction, srcIntent.getData(),
//	                getApplicationContext(), NicoroWebBrowser.class)
                    getApplicationContext(), MainFragmentActivity.class);
//	        launchIntent.putExtra(MainFragmentActivity.INTENT_EXTRA_MAIN_PANE_TYPE,
//	                MainFragmentActivity.MAIN_PANE_TYPE_BROWSER);
	        Set<String> categories = srcIntent.getCategories();
	        if (categories != null) {
	            for (String category : categories) {
	                launchIntent.addCategory(category);
	            }
	        }
	    } else {
            launchIntent = new Intent(
//                    getApplicationContext(), ListMenuActivity.class)
                    getApplicationContext(), DashBoardActivity.class);
	    }
	    launchIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        startActivity(launchIntent);
	}

	private void showHelp() {
        startActivityIfNeeded(
                new Intent(getApplicationContext(), NicoroHelp.class),
                0);
	}

	private class LoginButtonListener implements View.OnClickListener {
		@Override
		public void onClick(View v) {
			// 非同期処理に渡すので念のためコピー作成
			String mail = mInputMailAddress.getText().toString();
			String password = mInputPassword.getText().toString();

			mAsyncGetAuthorizeCookie = new AsyncGetAuthorizeCookie();
			mAsyncGetAuthorizeCookie.executeWrapper(mail, password);

			hideLoginView();
			showProgressView();
			if (mInputMethodManager == null) {
				Context context = getApplicationContext();
				mInputMethodManager = (InputMethodManager) context.getSystemService(
						Context.INPUT_METHOD_SERVICE);
			}
			mInputMethodManager.hideSoftInputFromWindow(
					mInputMailAddress.getWindowToken(), 0);
		}
	}

//	private class WithoutLoginButtonListener implements View.OnClickListener {
//		@Override
//		public void onClick(View v) {
//			endLogin();
//		}
//	}

	private class HelpButtonListener implements View.OnClickListener {
		@Override
		public void onClick(View v) {
		    showHelp();
		}
	}

    private static class SpaceTextWatcher implements TextWatcher {
        private final TextView mParent;
        private final String mError;
        private final Runnable mSetError = new Runnable() {
            @Override
            public void run() {
                mParent.setError(mError);
            }
        };
        private final Runnable mClearError = new Runnable() {
            @Override
            public void run() {
                mParent.setError(null);
            }
        };

        public SpaceTextWatcher(TextView tv, String err) {
            mParent = tv;
            mError = err;
        }
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
        @Override
        public void afterTextChanged(Editable s) {
            Handler handler = mParent.getHandler();
            if (handler != null) {
                if (s.toString().indexOf(' ') >= 0) {
                    handler.post(mSetError);
                } else {
                    handler.post(mClearError);
                }
            }
        }
    }
}
