using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Preferences;
using Android.Support.V7.App;
using Android.Text;
using Android.Views;
using Android.Widget;
using Java.IO;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using keepass2android.Io;
using Environment = Android.OS.Environment;
using IOException = Java.IO.IOException;

namespace keepass2android
{
	[Activity(Label = "@string/app_name",
			   ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden,
               Theme = "@style/MyTheme_ActionBar")]
	public class CreateDatabaseActivity : AppCompatActivity
	{
		private IOConnectionInfo _ioc;
		private string _keyfileFilename;
		private bool _restoringInstanceState;
		private bool _showPassword;

		private readonly ActivityDesign _design;
		private AppTask _appTask;

		public CreateDatabaseActivity()
		{
			_design = new ActivityDesign(this);
		}


		private const int RequestCodeKeyFile = 0;
		private const int RequestCodeDbFilename = 1;
		private const string KeyfilefilenameBundleKey = "KeyfileFilename";

		protected override void OnSaveInstanceState(Bundle outState)
		{
			base.OnSaveInstanceState(outState);
			outState.PutString(PasswordActivity.KeyFilename, _ioc.Path);
			outState.PutString(PasswordActivity.KeyServerusername, _ioc.UserName);
			outState.PutString(PasswordActivity.KeyServerpassword, _ioc.Password);
			outState.PutInt(PasswordActivity.KeyServercredmode, (int)_ioc.CredSaveMode);

			if (_keyfileFilename != null)
				outState.PutString(KeyfilefilenameBundleKey, _keyfileFilename);
		}

		protected override void OnCreate(Bundle bundle)
		{
			_design.ApplyTheme(); 
			base.OnCreate(bundle);
			

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

			SetContentView(Resource.Layout.create_database);
			_appTask = AppTask.GetTaskInOnCreate(bundle, Intent);

			SetDefaultIoc();

			FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
			

			var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);

			if (bundle != null)
			{
				_keyfileFilename = bundle.GetString(KeyfilefilenameBundleKey, null);
				if (_keyfileFilename != null)
				{
					FindViewById<TextView>(Resource.Id.keyfile_filename).Text = ConvertFilenameToIocPath(_keyfileFilename);
					FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
					keyfileCheckbox.Checked = true;
				}

				if (bundle.GetString(PasswordActivity.KeyFilename, null) != null)
				{
					_ioc = new IOConnectionInfo
						{
							Path = bundle.GetString(PasswordActivity.KeyFilename),
							UserName = bundle.GetString(PasswordActivity.KeyServerusername),
							Password = bundle.GetString(PasswordActivity.KeyServerpassword),
							CredSaveMode = (IOCredSaveMode) bundle.GetInt(PasswordActivity.KeyServercredmode),
						};
				}
			}

			UpdateIocView();

			keyfileCheckbox.CheckedChange += (sender, args) =>
				{
					if (keyfileCheckbox.Checked)
					{
						if (_restoringInstanceState)
							return;

						Util.ShowBrowseDialog(this, RequestCodeKeyFile, false, true);

					}
					else
					{
						FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Gone;
						_keyfileFilename = null;
					}
				};


			FindViewById(Resource.Id.btn_change_location).Click += (sender, args) =>
			{
				Intent intent = new Intent(this, typeof(FileStorageSelectionActivity));
				StartActivityForResult(intent, 0);
			};

			Button generatePassword = (Button)FindViewById(Resource.Id.generate_button);
			generatePassword.Click += (sender, e) =>
			{
				GeneratePasswordActivity.LaunchWithoutLockCheck(this);
			};

			FindViewById(Resource.Id.btn_create).Click += (sender, evt) => 
			{
				CreateDatabase();
			};

			ImageButton btnTogglePassword = (ImageButton)FindViewById(Resource.Id.toggle_password);
			btnTogglePassword.Click += (sender, e) =>
			{
				_showPassword = !_showPassword;
				MakePasswordMaskedOrVisible();
			};
			Android.Graphics.PorterDuff.Mode mMode = Android.Graphics.PorterDuff.Mode.SrcAtop;
			Android.Graphics.Color color = new Android.Graphics.Color (224, 224, 224);
			btnTogglePassword.SetColorFilter (color, mMode);

			
		}

		private void MakePasswordMaskedOrVisible()
		{
			TextView password = (TextView)FindViewById(Resource.Id.entry_password);
			TextView confpassword = (TextView)FindViewById(Resource.Id.entry_confpassword);
			if (_showPassword)
			{
				password.InputType = InputTypes.ClassText | InputTypes.TextVariationVisiblePassword;
				confpassword.Visibility = ViewStates.Gone;
			}
			else
			{
				password.InputType = InputTypes.ClassText | InputTypes.TextVariationPassword;
				confpassword.Visibility = ViewStates.Visible;
			}
			
		}

		private void CreateDatabase()
		{
			var keyfileCheckbox = FindViewById<CheckBox>(Resource.Id.use_keyfile);
			string password;
			if (!TryGetPassword(out password)) return;


			// Verify that a password or keyfile is set
			if (password.Length == 0 && !keyfileCheckbox.Checked)
			{
				Toast.MakeText(this, Resource.String.error_nopass, ToastLength.Long).Show();
				return;
			}

			//create the key
			CompositeKey newKey = new CompositeKey();
			if (String.IsNullOrEmpty(password) == false)
			{
				newKey.AddUserKey(new KcpPassword(password));
			}
			if (String.IsNullOrEmpty(_keyfileFilename) == false)
			{
				try
				{
					var ioc = IOConnectionInfo.FromPath(_keyfileFilename);
					using (var stream = App.Kp2a.GetFileStorage(ioc).OpenFileForRead(ioc))
					{
						byte[] keyfileData =  Util.StreamToMemoryStream(stream).ToArray();
						newKey.AddUserKey(new KcpKeyFile(keyfileData, ioc, true));
				}

				}
				catch (Exception)
				{
					Toast.MakeText(this, Resource.String.error_adding_keyfile, ToastLength.Long).Show();
					return;
				}
			}

			// Create the new database
			CreateDb create = new CreateDb(App.Kp2a, this, _ioc, new LaunchGroupActivity(_ioc, this), false, newKey);
			ProgressTask createTask = new ProgressTask(
				App.Kp2a,
				this, create);
			createTask.Run();
		}

		private bool TryGetPassword(out string pass)
		{
			TextView passView = (TextView)FindViewById(Resource.Id.entry_password);
			pass = passView.Text;

			if (_showPassword)
				return true;

			TextView passConfView = (TextView) FindViewById(Resource.Id.entry_confpassword);
			String confpass = passConfView.Text;

			// Verify that passwords match
			if (! pass.Equals(confpass))
			{
				// Passwords do not match
				Toast.MakeText(this, Resource.String.error_pass_match, ToastLength.Long).Show();
				return false;
			}
			return true;
		}

		protected override void OnResume()
		{
			base.OnResume();
			_design.ReapplyTheme();
		}

		protected override void OnRestoreInstanceState(Bundle savedInstanceState)
		{
			_restoringInstanceState = true;
			base.OnRestoreInstanceState(savedInstanceState);
			_restoringInstanceState = false;
		}


		private void UpdateIocView()
		{
			string displayPath = App.Kp2a.GetFileStorage(_ioc).GetDisplayName(_ioc);
			int protocolSeparatorPos = displayPath.IndexOf("://", StringComparison.Ordinal);
			string protocolId = protocolSeparatorPos < 0 ?
				"file" : displayPath.Substring(0, protocolSeparatorPos);
			Drawable drawable = App.Kp2a.GetResourceDrawable("ic_storage_" + protocolId);
			FindViewById<ImageView>(Resource.Id.filestorage_logo).SetImageDrawable(drawable);

			String title = App.Kp2a.GetResourceString("filestoragename_" + protocolId);
			FindViewById<TextView>(Resource.Id.filestorage_label).Text = title;

			FindViewById<TextView>(Resource.Id.label_filename).Text = protocolSeparatorPos < 0 ?
				displayPath :
				displayPath.Substring(protocolSeparatorPos + 3);

		}

		private void SetDefaultIoc()
		{
			File directory = GetExternalFilesDir(null);
			if (directory == null)
				directory = FilesDir;

			string strDir = directory.CanonicalPath;
			if (!strDir.EndsWith(File.Separator))
				strDir += File.Separator;

			string filename = strDir + "keepass.kdbx";
			filename = ConvertFilenameToIocPath(filename);
			int count = 2;
			while (new File(filename).Exists())
			{
				filename = ConvertFilenameToIocPath(strDir + "keepass" + count + ".kdbx");
				count++;
			}
			
			_ioc = new IOConnectionInfo
				{
					Path = filename
				};
		}

		private static string SdDir
		{
			get
			{
				string sdDir = Environment.ExternalStorageDirectory.AbsolutePath;
				if (!sdDir.EndsWith("/"))
					sdDir += "/";
				if (!sdDir.StartsWith("file://"))
					sdDir = "file://" + sdDir;
				return sdDir;
			}
		}

		protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
		{
			base.OnActivityResult(requestCode, resultCode, data);

			if (resultCode == KeePass.ResultOkPasswordGenerator)
			{
				String generatedPassword = data.GetStringExtra("keepass2android.password.generated_password");
				FindViewById<TextView>(Resource.Id.entry_password).Text = generatedPassword;
				FindViewById<TextView>(Resource.Id.entry_confpassword).Text = generatedPassword;
			}

			if (resultCode == KeePass.ExitFileStorageSelectionOk)
			{
				string protocolId = data.GetStringExtra("protocolId");
				if (protocolId == "content")
				{
					Util.ShowBrowseDialog(this, RequestCodeDbFilename, true, true);
				}
				else
				{
					FileSelectHelper fileSelectHelper = new FileSelectHelper(this, true, RequestCodeDbFilename)
					{
						DefaultExtension = "kdbx"
					};
					fileSelectHelper.OnOpen += (sender, info) =>
					{
						_ioc = info;
						UpdateIocView();
					};
					App.Kp2a.GetFileStorage(protocolId).StartSelectFile(
							new FileStorageSetupInitiatorActivity(this,OnActivityResult,s => fileSelectHelper.PerformManualFileSelect(s)), 
							true, 
							RequestCodeDbFilename, 
							protocolId);	
				}
				
			}

			if (resultCode == Result.Ok)
			{
				if (requestCode == RequestCodeKeyFile)
				{
					if (data.Data.Scheme == "content")
					{
						if ((int)Build.VERSION.SdkInt >= 19)
						{
							//try to take persistable permissions
							try
							{
								Kp2aLog.Log("TakePersistableUriPermission");
								var takeFlags = data.Flags
										& (ActivityFlags.GrantReadUriPermission
										| ActivityFlags.GrantWriteUriPermission);
								this.ContentResolver.TakePersistableUriPermission(data.Data, takeFlags);
							}
							catch (Exception e)
							{
								Kp2aLog.Log(e.ToString());
							}

						}
					}

					
					string filename = Util.IntentToFilename(data, this);
					if (filename == null)
						filename = data.DataString;

					
						_keyfileFilename = ConvertFilenameToIocPath(filename);
						FindViewById<TextView>(Resource.Id.keyfile_filename).Text = _keyfileFilename;
						FindViewById(Resource.Id.keyfile_filename).Visibility = ViewStates.Visible;
					
					}
				if (requestCode == RequestCodeDbFilename)
				{
					
					if (data.Data.Scheme == "content")
					{
						if ((int)Build.VERSION.SdkInt >= 19)
						{
							//try to take persistable permissions
							try
							{
								Kp2aLog.Log("TakePersistableUriPermission");
								var takeFlags = data.Flags
										& (ActivityFlags.GrantReadUriPermission
										| ActivityFlags.GrantWriteUriPermission);
								this.ContentResolver.TakePersistableUriPermission(data.Data, takeFlags);
							}
							catch (Exception e)
							{
								Kp2aLog.Log(e.ToString());
							}

						}
					}

					
					string filename = Util.IntentToFilename(data, this);
					if (filename == null)
						filename = data.DataString;

					bool fileExists = data.GetBooleanExtra("group.pals.android.lib.ui.filechooser.FileChooserActivity.result_file_exists", true);

					if (fileExists)
					{
						_ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(filename) };
						UpdateIocView();
					}
					else
					{
						var task = new CreateNewFilename(new ActionOnFinish((success, messageOrFilename) =>
							{
								if (!success)
								{
									Toast.MakeText(this, messageOrFilename, ToastLength.Long).Show();
									return;
								}
								_ioc = new IOConnectionInfo { Path = ConvertFilenameToIocPath(messageOrFilename) };
								UpdateIocView();
								
							}), filename);

						new ProgressTask(App.Kp2a, this, task).Run();
					}

				}
				
			}
			if (resultCode == (Result)FileStorageResults.FileUsagePrepared)
			{
				_ioc = new IOConnectionInfo();
				PasswordActivity.SetIoConnectionFromIntent(_ioc, data);
				UpdateIocView();
			}
			if (resultCode == (Result)FileStorageResults.FileChooserPrepared)
			{
				IOConnectionInfo ioc = new IOConnectionInfo();
				PasswordActivity.SetIoConnectionFromIntent(ioc, data);
				
				new FileSelectHelper(this, true, RequestCodeDbFilename) { DefaultExtension = "kdbx" }
					.StartFileChooser(ioc.Path);
				
			}

		}


		private static string ConvertFilenameToIocPath(string filename)
		{
			if ((filename != null) && (filename.StartsWith("file://")))
			{
				filename = filename.Substring(7);
				filename = Java.Net.URLDecoder.Decode(filename);
			}
			return filename;
		}


		private void AfterQueryCredentials(IOConnectionInfo ioc)
		{
			_ioc = ioc;
			UpdateIocView();	
		}

		private class LaunchGroupActivity : FileOnFinish
		{
			readonly CreateDatabaseActivity _activity;
			private readonly IOConnectionInfo _ioc;

			public LaunchGroupActivity(IOConnectionInfo ioc, CreateDatabaseActivity activity)
				: base(null)
			{
				_activity = activity;
				_ioc = ioc;
			}

			public override void Run()
			{
				if (Success)
				{
					// Update the ongoing notification
					App.Kp2a.UpdateOngoingNotification();

					if (PreferenceManager.GetDefaultSharedPreferences(_activity).GetBoolean(_activity.GetString(Resource.String.RememberRecentFiles_key), _activity.Resources.GetBoolean(Resource.Boolean.RememberRecentFiles_default))) 
					{
						// Add to recent files
						FileDbHelper dbHelper = App.Kp2a.FileDbHelper;


						//TODO: getFilename always returns "" -> bug?
						dbHelper.CreateFile(_ioc, Filename);
					}

					GroupActivity.Launch(_activity, _activity._appTask);
					_activity.Finish();

				}
				else
				{
					DisplayMessage(_activity);
					try
					{
						App.Kp2a.GetFileStorage(_ioc).Delete(_ioc);
					}
					catch (Exception e)
					{
						//not nice, but not a catastrophic failure if we can't delete the file:
						Kp2aLog.Log("couldn't delete file after failure! " + e);
					}
				}
			}
		}


	    public override bool OnOptionsItemSelected(IMenuItem item)
	    {
	        switch (item.ItemId)
	        {
	            case Android.Resource.Id.Home:
	                OnBackPressed();
	                return true;
	        }
	        return false;
	    }
	}
}