/*
 ============================================================================
 Name        : MainActivity.java
 Author      : hev <r@hev.cc>
 Copyright   : Copyright (c) 2023 xyz
 Description : Main Activity
 ============================================================================
 */

package org.iiab.controller;

import android.Manifest;
import android.os.Bundle;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.app.AlertDialog;
import android.content.Intent;
import android.content.Context;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import android.view.View;
import android.view.MotionEvent;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.net.VpnService;
import android.net.Uri;
import android.text.method.ScrollingMovementMethod;
import android.os.Build;
import android.os.Handler;
import android.os.PowerManager;
import android.provider.Settings;

import androidx.annotation.NonNull;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
	private static final String TAG = "IIAB-MainActivity";
	private static final String TERMUX_PERMISSION = "com.termux.permission.RUN_COMMAND";
	private Preferences prefs;
	private EditText edittext_socks_addr;
	private EditText edittext_socks_udp_addr;
	private EditText edittext_socks_port;
	private EditText edittext_socks_user;
	private EditText edittext_socks_pass;
	private EditText edittext_dns_ipv4;
	private EditText edittext_dns_ipv6;
	private CheckBox checkbox_udp_in_tcp;
	private CheckBox checkbox_remote_dns;
	private CheckBox checkbox_global;
	private CheckBox checkbox_ipv4;
	private CheckBox checkbox_ipv6;
	private Button button_apps;
	private Button button_save;
	private Button button_control;
	
	private Button watchdogControl;
	private TextView connectionLog;
	private LinearLayout logActions;
	private LinearLayout configLayout;
	private TextView configLabel;
	private LinearLayout advancedConfig;
	private TextView advConfigLabel;
	private TextView logLabel;
	private TextView logWarning;
	private TextView logSizeText;
	private ImageButton themeToggle;
	private TextView versionFooter;
	private ProgressBar logProgress;

	private ActivityResultLauncher<Intent> vpnPermissionLauncher;
	private ActivityResultLauncher<String> requestPermissionLauncher;
	private ActivityResultLauncher<String> notificationPermissionLauncher;
	
	private boolean isReadingLogs = false;
	private Handler sizeUpdateHandler = new Handler();
	private Runnable sizeUpdateRunnable;

	private final BroadcastReceiver logReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (IIABWatchdog.ACTION_LOG_MESSAGE.equals(intent.getAction())) {
				String message = intent.getStringExtra(IIABWatchdog.EXTRA_MESSAGE);
				addToLog(message);
				updateLogSizeUI(); 
			}
		}
	};

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

		prefs = new Preferences(this);
		setContentView(R.layout.main);

		// Initialize Result Launchers
		vpnPermissionLauncher = registerForActivityResult(
				new ActivityResultContracts.StartActivityForResult(),
				result -> {
					if (result.getResultCode() == RESULT_OK && prefs.getEnable()) {
						connectVpn();
					}
				}
		);

		requestPermissionLauncher = registerForActivityResult(
				new ActivityResultContracts.RequestPermission(),
				isGranted -> {
					if (isGranted) {
						addToLog("Termux RUN_COMMAND permission granted");
					} else {
						addToLog("Termux permission denied. Watchdog stimulus may fail.");
					}
				}
		);

		notificationPermissionLauncher = registerForActivityResult(
				new ActivityResultContracts.RequestPermission(),
				isGranted -> {
					if (isGranted) {
						addToLog("Notification permission granted");
					} else {
						addToLog("Notification permission denied. Status visibility may be limited.");
					}
				}
		);

		edittext_socks_addr = findViewById(R.id.socks_addr);
		edittext_socks_udp_addr = findViewById(R.id.socks_udp_addr);
		edittext_socks_port = findViewById(R.id.socks_port);
		edittext_socks_user = findViewById(R.id.socks_user);
		edittext_socks_pass = findViewById(R.id.socks_pass);
		edittext_dns_ipv4 = findViewById(R.id.dns_ipv4);
		edittext_dns_ipv6 = findViewById(R.id.dns_ipv6);
		checkbox_ipv4 = findViewById(R.id.ipv4);
		checkbox_ipv6 = findViewById(R.id.ipv6);
		checkbox_global = findViewById(R.id.global);
		checkbox_udp_in_tcp = findViewById(R.id.udp_in_tcp);
		checkbox_remote_dns = findViewById(R.id.remote_dns);
		button_apps = findViewById(R.id.apps);
		button_save = findViewById(R.id.save);
		button_control = findViewById(R.id.control);
		
		watchdogControl = findViewById(R.id.watchdog_control);
		watchdogControl.setOnClickListener(this);

		logActions = findViewById(R.id.log_actions);
		Button btnClearLog = findViewById(R.id.btn_clear_log);
		Button btnCopyLog = findViewById(R.id.btn_copy_log);
		btnClearLog.setOnClickListener(this);
		btnCopyLog.setOnClickListener(this);

		connectionLog = findViewById(R.id.connection_log);
		connectionLog.setMovementMethod(new ScrollingMovementMethod());
		connectionLog.setTextIsSelectable(true);
		
		logProgress = findViewById(R.id.log_progress);
		logWarning = findViewById(R.id.log_warning_text);
		logSizeText = findViewById(R.id.log_size_text);

		// Allow internal scrolling by disabling parent intercept
		connectionLog.setOnTouchListener((v, event) -> {
			v.getParent().requestDisallowInterceptTouchEvent(true);
			if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
				v.getParent().requestDisallowInterceptTouchEvent(false);
			}
			return false;
		});
		
		configLayout = findViewById(R.id.config_layout);
		configLabel = findViewById(R.id.config_label);
		configLabel.setOnClickListener(v -> toggleVisibility(configLayout, configLabel, getString(R.string.configuration_label)));

		advancedConfig = findViewById(R.id.advanced_config);
		advConfigLabel = findViewById(R.id.adv_config_label);
		advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, getString(R.string.advanced_settings_label)));

		logLabel = findViewById(R.id.log_label);
		logLabel.setOnClickListener(v -> {
			boolean isOpening = connectionLog.getVisibility() == View.GONE;
			if (isOpening) {
				readBlackBoxLogs(); 
				startLogSizeUpdates();
			} else {
				stopLogSizeUpdates();
			}
			toggleVisibility(connectionLog, logLabel, getString(R.string.connection_log_label));
			logActions.setVisibility(connectionLog.getVisibility());
			if (logSizeText != null) logSizeText.setVisibility(connectionLog.getVisibility());
		});

		themeToggle = findViewById(R.id.theme_toggle);
		themeToggle.setOnClickListener(v -> toggleTheme());
		applySavedTheme();

		versionFooter = findViewById(R.id.version_text);
		setVersionFooter();

		checkbox_udp_in_tcp.setOnClickListener(this);
		checkbox_remote_dns.setOnClickListener(this);
		checkbox_global.setOnClickListener(this);
		button_apps.setOnClickListener(this);
		button_save.setOnClickListener(this);
		button_control.setOnClickListener(this);
		updateUI();

		/* Request permissions */
		checkNotificationPermission();
		checkTermuxPermission();
		checkBatteryOptimizations();

		/* Request VPN permission */
		Intent intent = VpnService.prepare(MainActivity.this);
		if (intent != null) {
			vpnPermissionLauncher.launch(intent);
		} else if (prefs.getEnable()) {
			connectVpn();
		}
		
		addToLog(getString(R.string.app_started));
		
		sizeUpdateRunnable = new Runnable() {
			@Override
			public void run() {
				updateLogSizeUI();
				sizeUpdateHandler.postDelayed(this, 10000); 
			}
		};
	}

	private void checkNotificationPermission() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
			if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
				notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS);
			}
		}
	}

	private void checkTermuxPermission() {
		if (ContextCompat.checkSelfPermission(this, TERMUX_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
			requestPermissionLauncher.launch(TERMUX_PERMISSION);
		}
	}

	private void startLogSizeUpdates() {
		sizeUpdateHandler.removeCallbacks(sizeUpdateRunnable);
		sizeUpdateHandler.post(sizeUpdateRunnable);
	}

	private void stopLogSizeUpdates() {
		sizeUpdateHandler.removeCallbacks(sizeUpdateRunnable);
	}

	private void updateLogSizeUI() {
		if (logSizeText == null) return;
		File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
		long size = logFile.exists() ? logFile.length() : 0;
		String sizeStr;
		if (size < 1024) {
			sizeStr = size + " B";
		} else if (size < 1024 * 1024) {
			sizeStr = String.format(Locale.getDefault(), "%.1f KB", size / 1024.0);
		} else {
			sizeStr = String.format(Locale.getDefault(), "%.2f MB", size / (1024.0 * 1024.0));
		}
		logSizeText.setText(getString(R.string.log_size_format, sizeStr));
	}

	private void connectVpn() {
		Intent intent = new Intent(this, TProxyService.class);
		startService(intent.setAction(TProxyService.ACTION_CONNECT));
		addToLog(getString(R.string.vpn_permission_granted));
	}

	private void readBlackBoxLogs() {
		if (isReadingLogs) return;
		isReadingLogs = true;
		
		if (logProgress != null) {
			logProgress.setVisibility(View.VISIBLE);
		}

		new Thread(() -> {
			File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
			StringBuilder sb = new StringBuilder();
			
			if (!logFile.exists()) {
				sb.append(getString(R.string.no_blackbox_found)).append("\n");
			} else {
				sb.append(getString(R.string.loading_history)).append("\n");
				try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
					String line;
					while ((line = br.readLine()) != null) {
						sb.append(line).append("\n");
					}
				} catch (IOException e) {
					sb.append(getString(R.string.error_reading_history, e.getMessage())).append("\n");
				}
				sb.append(getString(R.string.end_of_history)).append("\n");
			}

			final String result = sb.toString();
			
			// Check rapid growth flag
			SharedPreferences internalPrefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
			final boolean isRapid = internalPrefs.getBoolean(IIABWatchdog.PREF_RAPID_GROWTH, false);

			runOnUiThread(() -> {
				if (connectionLog != null) {
					connectionLog.setText(result);
					scrollToBottom();
				}
				if (logProgress != null) {
					logProgress.setVisibility(View.GONE);
				}
				if (logWarning != null) {
					logWarning.setVisibility(isRapid ? View.VISIBLE : View.GONE);
				}
				updateLogSizeUI();
				isReadingLogs = false;
			});
		}).start();
	}

	private void setVersionFooter() {
		try {
			PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
			String version = pInfo.versionName;
			versionFooter.setText(version);
		} catch (PackageManager.NameNotFoundException e) {
			versionFooter.setText("v0.1.x");
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		stopLogSizeUpdates();
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (getIntent() != null && getIntent().getBooleanExtra(VpnRecoveryReceiver.EXTRA_RECOVERY, false)) {
			addToLog(getString(R.string.recovery_pulse_received));
			Intent vpnIntent = new Intent(this, TProxyService.class);
			startService(vpnIntent.setAction(TProxyService.ACTION_CONNECT));
			setIntent(null);
		}
		if (connectionLog != null && connectionLog.getVisibility() == View.VISIBLE) {
			startLogSizeUpdates();
		}
	}

	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		setIntent(intent);
	}

	private void checkBatteryOptimizations() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            if (pm != null && !pm.isIgnoringBatteryOptimizations(getPackageName())) {
                new AlertDialog.Builder(this)
                    .setTitle(R.string.battery_opt_title)
                    .setMessage(R.string.battery_opt_msg)
                    .setPositiveButton(R.string.go_to_settings, (dialog, which) -> {
                        Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                        intent.setData(Uri.parse("package:" + getPackageName()));
                        startActivity(intent);
                    })
                    .setNegativeButton(R.string.cancel, null)
                    .show();
            }
        }
    }

	private void toggleTheme() {
		SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
		int currentMode = AppCompatDelegate.getDefaultNightMode();
		int nextMode;

		if (currentMode == AppCompatDelegate.MODE_NIGHT_NO) {
			nextMode = AppCompatDelegate.MODE_NIGHT_YES;
		} else if (currentMode == AppCompatDelegate.MODE_NIGHT_YES) {
			nextMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
		} else {
			nextMode = AppCompatDelegate.MODE_NIGHT_NO;
		}

		SharedPreferences.Editor editor = sharedPref.edit();
		editor.putInt("ui_mode", nextMode);
		editor.apply();

		AppCompatDelegate.setDefaultNightMode(nextMode);
		updateThemeToggleButton(nextMode);
	}

	private void applySavedTheme() {
		SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
		int savedMode = sharedPref.getInt("ui_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
		AppCompatDelegate.setDefaultNightMode(savedMode);
		updateThemeToggleButton(savedMode);
	}

	private void updateThemeToggleButton(int mode) {
		if (mode == AppCompatDelegate.MODE_NIGHT_NO) {
			themeToggle.setImageResource(R.drawable.ic_theme_dark);
		} else if (mode == AppCompatDelegate.MODE_NIGHT_YES) {
			themeToggle.setImageResource(R.drawable.ic_theme_light);
		} else {
			themeToggle.setImageResource(R.drawable.ic_theme_system);
		}
	}

	private void toggleVisibility(View view, TextView label, String text) {
		if (view.getVisibility() == View.GONE) {
			view.setVisibility(View.VISIBLE);
			label.setText("▼ " + text);
		} else {
			view.setVisibility(View.GONE);
			label.setText("▶ " + text);
		}
	}

	@Override
	protected void onStart() {
		super.onStart();
		IntentFilter filter = new IntentFilter(IIABWatchdog.ACTION_LOG_MESSAGE);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
			registerReceiver(logReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
		} else {
			registerReceiver(logReceiver, filter);
		}
	}

	@Override
	protected void onStop() {
		super.onStop();
		try {
			unregisterReceiver(logReceiver);
		} catch (Exception e) {
			// Ignore
		}
		stopLogSizeUpdates();
	}

	@Override
	public void onClick(View view) {
		if (view == checkbox_global || view == checkbox_remote_dns) {
			savePrefs();
			updateUI();
		} else if (view == button_apps) {
			startActivity(new Intent(this, AppListActivity.class));
		} else if (view.getId() == R.id.save) {
			savePrefs();
			Toast.makeText(this, R.string.saved_toast, Toast.LENGTH_SHORT).show();
			addToLog(getString(R.string.settings_saved));
		} else if (view.getId() == R.id.control) {
			handleControlClick();
		} else if (view.getId() == R.id.watchdog_control) {
			handleWatchdogClick();
		} else if (view.getId() == R.id.btn_clear_log) {
			showResetLogConfirmation();
		} else if (view.getId() == R.id.btn_copy_log) {
			ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
			ClipData clip = ClipData.newPlainText("IIAB Log", connectionLog.getText().toString());
			if (clipboard != null) {
				clipboard.setPrimaryClip(clip);
				Toast.makeText(this, R.string.log_copied_toast, Toast.LENGTH_SHORT).show();
			}
		}
	}

	private void showResetLogConfirmation() {
		new AlertDialog.Builder(this)
			.setTitle(R.string.log_reset_confirm_title)
			.setMessage(R.string.log_reset_confirm_msg)
			.setPositiveButton(R.string.reset_log, (dialog, which) -> resetLogFile())
			.setNegativeButton(R.string.cancel, null)
			.show();
	}

	private void resetLogFile() {
		File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
		try (PrintWriter pw = new PrintWriter(logFile)) {
			pw.print(""); // Truncate file to 0 bytes
			connectionLog.setText("");
			addToLog(getString(R.string.log_reset_user));
			
			// Clear rapid growth warning
			SharedPreferences internalPrefs = getSharedPreferences("IIAB_Internal", Context.MODE_PRIVATE);
			internalPrefs.edit().putBoolean(IIABWatchdog.PREF_RAPID_GROWTH, false).apply();
			if (logWarning != null) logWarning.setVisibility(View.GONE);
			
			updateLogSizeUI();
			Toast.makeText(this, R.string.log_cleared_toast, Toast.LENGTH_SHORT).show();
		} catch (IOException e) {
			Toast.makeText(this, getString(R.string.failed_reset_log, e.getMessage()), Toast.LENGTH_SHORT).show();
		}
	}

	private void handleWatchdogClick() {
		boolean isEnabled = prefs.getWatchdogEnable();
		if (isEnabled) {
			showWatchdogBiometricPrompt();
		} else {
			toggleWatchdog(false);
		}
	}

	private void toggleWatchdog(boolean stop) {
		prefs.setWatchdogEnable(!stop);
		Intent intent = new Intent(this, WatchdogService.class);
		if (stop) {
			stopService(intent);
			addToLog(getString(R.string.watchdog_stopped));
		} else {
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
				startForegroundService(intent.setAction(WatchdogService.ACTION_START));
			} else {
				startService(intent.setAction(WatchdogService.ACTION_START));
			}
			addToLog(getString(R.string.watchdog_started));
		}
		updateUI();
	}

	private void handleControlClick() {
		boolean isCurrentlyEnabled = prefs.getEnable();
		if (isCurrentlyEnabled) {
			showBiometricPrompt();
		} else {
			BiometricManager biometricManager = BiometricManager.from(this);

			int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
			if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
				authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK | BiometricManager.Authenticators.DEVICE_CREDENTIAL;
			}

			boolean isSecure = false;
			android.app.KeyguardManager km = (android.app.KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
			if (km != null && km.isDeviceSecure()) isSecure = true;

			if (biometricManager.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS || isSecure) {
				addToLog(getString(R.string.user_initiated_conn));
				toggleService(false);
			} else {
				showEnrollmentDialog();
			}
		}
	}

	private void showEnrollmentDialog() {
		new AlertDialog.Builder(this)
			.setTitle(R.string.security_required_title)
			.setMessage(R.string.security_required_msg)
			.setPositiveButton(R.string.go_to_settings, (dialog, which) -> {
				Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
				startActivity(intent);
			})
			.setNegativeButton(R.string.cancel, null)
			.show();
	}

	private void showBiometricPrompt() {
		Executor executor = ContextCompat.getMainExecutor(this);
		BiometricPrompt biometricPrompt = new BiometricPrompt(MainActivity.this,
				executor, new BiometricPrompt.AuthenticationCallback() {
			@Override
			public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
				super.onAuthenticationSucceeded(result);
				addToLog(getString(R.string.auth_success_disconnect));
				toggleService(true);
			}
		});

		int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;

		BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
				.setTitle(getString(R.string.auth_required_title))
				.setSubtitle(getString(R.string.auth_required_subtitle))
				.setAllowedAuthenticators(authenticators)
				.build();

		biometricPrompt.authenticate(promptInfo);
	}

	private void showWatchdogBiometricPrompt() {
		Executor executor = ContextCompat.getMainExecutor(this);
		BiometricPrompt biometricPrompt = new BiometricPrompt(this, executor, new BiometricPrompt.AuthenticationCallback() {
			@Override
			public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
				super.onAuthenticationSucceeded(result);
				toggleWatchdog(true);
			}
		});

		int authenticators = BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL;

		BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
				.setTitle(getString(R.string.unlock_watchdog_title))
				.setSubtitle(getString(R.string.unlock_watchdog_subtitle))
				.setAllowedAuthenticators(authenticators)
				.build();

		biometricPrompt.authenticate(promptInfo);
	}

	private void toggleService(boolean isEnable) {
		prefs.setEnable(!isEnable);
		savePrefs();
		updateUI();
		Intent intent = new Intent(this, TProxyService.class);
		if (isEnable) {
			startService(intent.setAction(TProxyService.ACTION_DISCONNECT));
			addToLog(getString(R.string.vpn_stopping));
		} else {
			startService(intent.setAction(TProxyService.ACTION_CONNECT));
			addToLog(getString(R.string.vpn_starting));
		}
	}

	private void updateUI() {
		boolean vpnActive = prefs.getEnable();
		boolean watchdogActive = prefs.getWatchdogEnable();

		if (vpnActive) {
			button_control.setText(R.string.control_disable);
			button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on));
		} else {
			button_control.setText(R.string.control_enable);
			button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off));
		}

		if (watchdogActive) {
			watchdogControl.setText(R.string.watchdog_disable);
			watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
		} else {
			watchdogControl.setText(R.string.watchdog_enable);
			watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off));
		}
		
		edittext_socks_addr.setText(prefs.getSocksAddress());
		edittext_socks_udp_addr.setText(prefs.getSocksUdpAddress());
		edittext_socks_port.setText(String.valueOf(prefs.getSocksPort()));
		edittext_socks_user.setText(prefs.getSocksUsername());
		edittext_socks_pass.setText(prefs.getSocksPassword());
		edittext_dns_ipv4.setText(prefs.getDnsIpv4());
		edittext_dns_ipv6.setText(prefs.getDnsIpv6());
		checkbox_ipv4.setChecked(prefs.getIpv4());
		checkbox_ipv6.setChecked(prefs.getIpv6());
		checkbox_global.setChecked(prefs.getGlobal());
		checkbox_udp_in_tcp.setChecked(prefs.getUdpInTcp());
		checkbox_remote_dns.setChecked(prefs.getRemoteDns());

		boolean editable = !vpnActive;
		edittext_socks_addr.setEnabled(editable);
		edittext_socks_port.setEnabled(editable);
		button_save.setEnabled(editable);
	}

	private void savePrefs() {
		prefs.setSocksAddress(edittext_socks_addr.getText().toString());
		prefs.setSocksPort(Integer.parseInt(edittext_socks_port.getText().toString()));
		prefs.setSocksUdpAddress(edittext_socks_udp_addr.getText().toString());
		prefs.setSocksUsername(edittext_socks_user.getText().toString());
		prefs.setSocksPassword(edittext_socks_pass.getText().toString());
		prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString());
		prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString());
		prefs.setIpv4(checkbox_ipv4.isChecked());
		prefs.setIpv6(checkbox_ipv6.isChecked());
		prefs.setGlobal(checkbox_global.isChecked());
		prefs.setUdpInTcp(checkbox_udp_in_tcp.isChecked());
		prefs.setRemoteDns(checkbox_remote_dns.isChecked());
	}
	
	private void addToLog(String message) {
		runOnUiThread(() -> {
			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
			String currentTime = sdf.format(new Date());
			String logEntry = "[" + currentTime + "] " + message + "\n";
			if (connectionLog != null) {
				connectionLog.append(logEntry);
				scrollToBottom();
			}
		});
	}

	private void scrollToBottom() {
		if (connectionLog.getLayout() != null) {
			final int scrollAmount = connectionLog.getLayout().getLineTop(connectionLog.getLineCount()) - connectionLog.getHeight();
			if (scrollAmount > 0)
				connectionLog.scrollTo(0, scrollAmount);
		}
	}
}
