using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Hardware.Fingerprints;
using Android.OS;
using Android.Preferences;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Google.Android.Material.Dialog;
using Java.Lang;
using keepass2android;
using KeePassLib.Keys;
using KeePassLib.Utility;
using Enum = System.Enum;
using Exception = System.Exception;

namespace keepass2android
{
	[Activity(Label = "@string/app_name",
	    ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden,
		Theme = "@style/Kp2aTheme_ActionBar", MainLauncher = false, Exported = true)]
	[IntentFilter(new[] { "kp2a.action.FingerprintSetupActivity" }, Categories = new[] { Intent.CategoryDefault })]
	public class BiometricSetupActivity : LockCloseActivity, IBiometricAuthCallback
	{
		private readonly ActivityDesign _activityDesign;

		public BiometricSetupActivity(IntPtr javaReference, JniHandleOwnership transfer)
			: base(javaReference, transfer)
	    {
		    _activityDesign = new ActivityDesign(this);
	    }
		public BiometricSetupActivity()
		{
			_activityDesign = new ActivityDesign(this);
		}

		

		private FingerprintUnlockMode _unlockMode = FingerprintUnlockMode.Disabled;
		private FingerprintUnlockMode _desiredUnlockMode;
		private BiometricEncryption _enc;
		private RadioButton[] _radioButtons;
		public override bool OnOptionsItemSelected(IMenuItem item)
		{
			switch (item.ItemId)
			{

				case Android.Resource.Id.Home:
					Finish();
					return true;
			}

			return base.OnOptionsItemSelected(item);
		}

		protected override void OnCreate(Bundle savedInstanceState)
		{
			_activityDesign.ApplyTheme();
			base.OnCreate(savedInstanceState);
			SetContentView(Resource.Layout.fingerprint_setup);

			Enum.TryParse(
				PreferenceManager.GetDefaultSharedPreferences(this).GetString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, ""),
				out _unlockMode);

			_fpIcon = FindViewById<ImageView>(Resource.Id.fingerprint_icon);
			_fpTextView = FindViewById<TextView>(Resource.Id.fingerprint_status);

            
			SupportActionBar.SetDisplayHomeAsUpEnabled(true);
			SupportActionBar.SetHomeButtonEnabled(true);

			int[] radioButtonIds =
			{
				Resource.Id.radio_fingerprint_quickunlock, Resource.Id.radio_fingerprint_unlock,
				Resource.Id.radio_fingerprint_disabled
			};
			_radioButtons = radioButtonIds.Select(FindViewById<RadioButton>).ToArray();
			_radioButtons[0].Tag = FingerprintUnlockMode.QuickUnlock.ToString();
			_radioButtons[1].Tag = FingerprintUnlockMode.FullUnlock.ToString();
			_radioButtons[2].Tag = FingerprintUnlockMode.Disabled.ToString();
			foreach (RadioButton r in _radioButtons)
			{
				r.CheckedChange += (sender, args) =>
				{
					var rbSender = ((RadioButton) sender);
					if (!rbSender.Checked) return;
					foreach (RadioButton rOther in _radioButtons)
					{
						if (rOther == sender) continue;
						rOther.Checked = false;
					}
					FingerprintUnlockMode newMode;
					Enum.TryParse(rbSender.Tag.ToString(), out newMode);
					ChangeUnlockMode(_unlockMode, newMode);
					
				};
			}

			CheckCurrentRadioButton();

			int errorId = Resource.String.fingerprint_os_error;
			SetError(errorId);

			FindViewById(Resource.Id.cancel_button).Click += (sender, args) =>
			{
				_enc.StopListening();
				_unlockMode = FingerprintUnlockMode.Disabled; //cancelling a FingerprintEncryption means a new key has been created but not been authenticated to encrypt something. We can't keep the previous state.
				StoreUnlockMode();
				FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
				FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
				_enc = null;
				CheckCurrentRadioButton();
			};

			FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
			FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;

            FindViewById<CheckBox>(Resource.Id.close_database_after_failed).Checked =
                Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this);

            FindViewById<CheckBox>(Resource.Id.close_database_after_failed).CheckedChange += (sender, args) =>
            {
                PreferenceManager.GetDefaultSharedPreferences(this)
                    .Edit()
                    .PutBoolean(GetString(Resource.String.CloseDatabaseAfterFailedBiometricQuickUnlock_key), args.IsChecked)
                    .Commit();
            };


            UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();


        }

		private void UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility()
		{
			FindViewById(Resource.Id.close_database_after_failed).Visibility = _unlockMode == FingerprintUnlockMode.QuickUnlock ? ViewStates.Visible : ViewStates.Gone;
		}
		
		string CurrentPreferenceKey
		{
			get { return App.Kp2a.CurrentDb.CurrentFingerprintPrefKey; }
		}

		private void StoreUnlockMode()
		{
			ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
			if (_unlockMode == FingerprintUnlockMode.Disabled)
			{
				edit.PutString(CurrentPreferenceKey, "");
			}
			else
			{
			    try
			    {
			        if (_unlockMode == FingerprintUnlockMode.FullUnlock)
			        {
			            var userKey = App.Kp2a.CurrentDb.KpDatabase.MasterKey.GetUserKey<KcpPassword>();
			            _enc.StoreEncrypted(userKey != null ? userKey.Password.ReadString() : "", CurrentPreferenceKey, edit);
			        }
			        else
			            _enc.StoreEncrypted("QuickUnlock" /*some dummy data*/, CurrentPreferenceKey, edit);
                }
			    catch (Exception e)
			    {
			        new MaterialAlertDialogBuilder(this)
			            .SetTitle(GetString(Resource.String.ErrorOcurred))
			            .SetMessage(GetString(Resource.String.FingerprintSetupFailed))
                        .SetCancelable(false)
                        .SetPositiveButton(Android.Resource.String.Ok, (sender, args) => { })
                        .Show();

                }

            }
			edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, _unlockMode.ToString());
			edit.Commit();
		}

		private void CheckCurrentRadioButton()
		{
			
			foreach (RadioButton r in _radioButtons)
			{
				FingerprintUnlockMode um;
				Enum.TryParse(r.Tag.ToString(), out um);
				if (um == _unlockMode)
					r.Checked = true;
			}
		}

		private void SetError(int errorId)
		{
			var tv = FindViewById<TextView>(Resource.Id.tvFatalError);
			tv.Text = GetString(Resource.String.fingerprint_fatal) + " " + GetString(errorId);
			tv.Visibility = ViewStates.Visible;
		}

	
		private void ShowRadioButtons()
		{
			FindViewById<TextView>(Resource.Id.tvFatalError).Visibility = ViewStates.Gone;
			FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
			FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
		}

        private void HideRadioButtons()
        {
            FindViewById<TextView>(Resource.Id.tvFatalError).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
            FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
        }


        private void ChangeUnlockMode(FingerprintUnlockMode oldMode, FingerprintUnlockMode newMode)
		{
			if (oldMode == newMode)
				return;

			
			if (newMode == FingerprintUnlockMode.Disabled)
			{
				_unlockMode = newMode;
                UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();

                StoreUnlockMode();
				return;
			}

			_desiredUnlockMode = newMode;
			FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
            UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();

            FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
            try
			{
                _enc = new BiometricEncryption(new BiometricModule(this), CurrentPreferenceKey);
                if (!_enc.Init())
					throw new Exception("Failed to initialize cipher");
				ResetErrorTextRunnable();
                
                _enc.StartListening(new BiometricAuthCallbackAdapter(this, this));
            }
			catch (Exception e)
			{
				CheckCurrentRadioButton();
				Toast.MakeText(this, e.ToString(), ToastLength.Long).Show();
				FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
				FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
			}
			

		}

		static readonly long ERROR_TIMEOUT_MILLIS = 1600;
		static readonly long SUCCESS_DELAY_MILLIS = 1300;
		private ImageView _fpIcon;
		private TextView _fpTextView;
		
		public void OnBiometricAuthSucceeded()
		{
			_unlockMode = _desiredUnlockMode;

			_fpTextView.RemoveCallbacks(ResetErrorTextRunnable);
			_fpIcon.SetImageResource(Resource.Drawable.ic_fingerprint_success);
			_fpTextView.SetTextColor(_fpTextView.Resources.GetColor(Resource.Color.md_theme_secondary, null));
			_fpTextView.Text = _fpTextView.Resources.GetString(Resource.String.fingerprint_success);
			_fpIcon.PostDelayed(() =>
			{
				FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Visible;
				FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;

				StoreUnlockMode();
				UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
			

			}, SUCCESS_DELAY_MILLIS);

			
		}


		
		public void OnBiometricError(string error)
		{
			_fpIcon.SetImageResource(Resource.Drawable.ic_fingerprint_error);
			_fpTextView.Text = error;
			_fpTextView.SetTextColor(
				_fpTextView.Resources.GetColor(Resource.Color.md_theme_error, null));
			_fpTextView.RemoveCallbacks(ResetErrorTextRunnable);
			_fpTextView.PostDelayed(ResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
		}

        public void OnBiometricAttemptFailed(string message)
        {
            //ignore
        }

        void ResetErrorTextRunnable()
		{
			_fpTextView.SetTextColor(
				_fpTextView.Resources.GetColor(Resource.Color.md_theme_secondary, null));
			_fpTextView.Text = "";
			_fpIcon.SetImageResource(Resource.Drawable.baseline_fingerprint_24);
		}

		protected override void OnResume()
		{
			base.OnResume();

            BiometricModule fpModule = new BiometricModule(this);
            HideRadioButtons();
            if (!fpModule.IsHardwareAvailable)
            {
                SetError(Resource.String.fingerprint_hardware_error);
                UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
                return;
            }
            if (!fpModule.IsAvailable)
            {
                SetError(Resource.String.fingerprint_no_enrolled);
                return;
            }
            ShowRadioButtons();
            UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();

        }

		protected override void OnPause()
		{
			base.OnPause();
			if (_enc != null)
				_enc.StopListening();
		}
	}

}