package com.wytehat.btlogger;

import android.app.*;
import android.content.*;
import android.content.pm.*;
import android.graphics.*;
import android.net.*;
import android.os.*;
import android.provider.*;
import android.view.*;
import android.widget.*;
import java.io.*;
import java.lang.reflect.*;

public class MainActivity extends Activity implements ItemActionListener {
    private static final int PERMISSIONS = 100;
    private static final int CAMERA = 200;
    private TrackerDatabase db;
    private DeviceListAdapter adapter;
    private boolean reverseSort;
    private boolean registered;
    private DeviceRecord editing;
    private final Handler refreshHandler = new Handler();
    private final Runnable refreshLiveAvailability = new Runnable() {
        public void run() { reload(); refreshHandler.postDelayed(this, 10000L); }
    };

    private final BroadcastReceiver updates = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) { reload(); }
    };

    protected void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.main);
        LargeScreenLayout.constrain(this);
        LinearLayout nav = (LinearLayout) findViewById(R.id.navigation_container);
        nav.addView(NavigationHelper.createBar(this, 0, "My Tracked Items"));
        db = new TrackerDatabase(this);
        ListView list = (ListView) findViewById(R.id.device_list);
        list.setEmptyView(findViewById(R.id.empty_view));
        adapter = new DeviceListAdapter(this, db.tracked(), this);
        list.setAdapter(adapter);
        findViewById(R.id.devices_button).setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) { startActivity(new Intent(MainActivity.this,
																	   DeviceManagerActivity.class)); }
			});
        findViewById(R.id.map_all_button).setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) { startActivity(new Intent(MainActivity.this,
																	   MapActivity.class)); }
			});
        reverseSort = getSharedPreferences("list_settings", 0)
            .getBoolean("reverse_sort", false);
        updateSortButton();
        findViewById(R.id.sort_button).setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) {
					reverseSort = !reverseSort;
					getSharedPreferences("list_settings", 0).edit()
						.putBoolean("reverse_sort", reverseSort).apply();
					updateSortButton();
					reload();
				}
			});
        findViewById(R.id.settings_button).setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) { showSettings(); }
			});
        requestNeededPermissions();
        startTracker();
    }

    protected void onResume() {
        super.onResume();
        registerReceiver(updates, new IntentFilter(BluetoothTrackingService.ACTION_DATA_CHANGED));
        registered = true;
        refreshHandler.removeCallbacks(refreshLiveAvailability);
        refreshHandler.post(refreshLiveAvailability);
        reload();
    }

    protected void onPause() {
        refreshHandler.removeCallbacks(refreshLiveAvailability);
        if (registered) { unregisterReceiver(updates); registered = false; }
        super.onPause();
    }

    protected void onDestroy() { db.close(); super.onDestroy(); }

    public void edit(final DeviceRecord record) {
        editing = record;
        String[] choices = { "Rename item", "Take item photo", "Headphones icon",
            "Keys icon", "Vehicle icon", "Wallet icon", "Bag icon",
            "Tracking frequency and pin color", record.locationLocked == 1 ? "Re-find this found item" : "Find this item", record.showTrail == 1 ? "Hide location trail" : "Show location trail", record.trackBroadcast == 1 ? "Stop tracking while broadcasting" : "Track while broadcasting", "Remove from tracked items" };
        new AlertDialog.Builder(this).setTitle("Edit " + record.displayName())
            .setItems(choices, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    if (which == 0) rename(record);
                    else if (which == 1) takePhoto();
                    else if (which == 7) chooseTracking(record);
                    else if (which == 8) refind(record);
                    else if (which == 9) toggleTrail(record);
                    else if (which == 10) toggleBroadcastTracking(record);
                    else if (which == 11) removeTrackedItem(record);
                    else {
                        String[] icons = { "", "", "headphones", "keys", "car",
                            "wallet", "bag" };
                        db.updateProfile(record.address, null, null, icons[which]);
                        reload();
                    }
                }
            }).show();
    }

    private void removeTrackedItem(final DeviceRecord record) {
        new AlertDialog.Builder(this).setTitle("Remove from tracked items?")
            .setMessage("The item will disappear from the dashboard and map. Its sightings and location history will remain saved, and it can be added again when its current Bluetooth ID is seen.")
            .setPositiveButton("REMOVE", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    db.removeFromTracking(record.address);
                    sendBroadcast(new Intent(BluetoothTrackingService.ACTION_CONFIG_CHANGED).setPackage(getPackageName()));
                    reload();
                    Toast.makeText(MainActivity.this, "Removed from tracked items; history retained", Toast.LENGTH_LONG).show();
                }
            }).setNegativeButton("CANCEL", null).show();
    }

    private void toggleBroadcastTracking(DeviceRecord record) {
        boolean enabled = record.trackBroadcast != 1;
        db.setTrackBroadcast(record.address, enabled);
        sendBroadcast(new Intent(BluetoothTrackingService.ACTION_CONFIG_CHANGED)
					  .setPackage(getPackageName()));
        Toast.makeText(this, enabled ? "Will log GPS while this item is broadcasting" :
					   "Broadcast location tracking disabled", Toast.LENGTH_LONG).show();
        reload();
    }

    private void toggleTrail(DeviceRecord record) {
        boolean show = record.showTrail != 1;
        db.setShowTrail(record.address, show);
        Toast.makeText(this, show ? "Location trail shown" : "Location trail hidden", Toast.LENGTH_SHORT).show();
        reload();
    }

    private void refind(DeviceRecord record) {
        DeviceRecord current = db.get(record.address);
        if (current == null || current.rssi == 0 ||
            System.currentTimeMillis() - current.lastRssiAt > 120000L) {
            Toast.makeText(this, "Re-find needs a current BLE signal. Select the item in Bluetooth Radar first.", Toast.LENGTH_LONG).show();
            return;
        }
        db.setLocationLocked(record.address, false);
        db.setLiveEnabled(record.address, true);
        db.setMapEnabled(record.address, true);
        live(current);
    }

    public void map(DeviceRecord record) {
        Intent intent = new Intent(this, MapActivity.class);
        intent.putExtra("focus", record.address);
        startActivity(intent);
    }

    public void live(DeviceRecord record) {
        DeviceRecord current = db.get(record.address);
        if (current == null || current.rssi == 0 || System.currentTimeMillis() - current.lastRssiAt > 120000L) {
            Toast.makeText(this, "Live Find needs a recent BLE signal. Scan on Bluetooth Radar first.", Toast.LENGTH_LONG).show();
            reload();
            return;
        }
        Intent intent = new Intent(this, LiveRangeFinderActivity.class);
        intent.putExtra("address", record.address);
        startActivity(intent);
    }

    public void manage(DeviceRecord record) {
        Intent intent = new Intent(this, DeviceManagerActivity.class);
        intent.putExtra("focus", record.address);
        startActivity(intent);
    }

    private void rename(final DeviceRecord record) {
        final EditText input = new EditText(this);
        input.setText(record.displayName());
        new AlertDialog.Builder(this).setTitle("Item name").setView(input)
            .setPositiveButton("SAVE", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    db.updateProfile(record.address, input.getText().toString(),
									 record.photoUri, record.iconPreset);
                    reload();
                }
            }).setNegativeButton("CANCEL", null).show();
    }

    private void chooseTracking(final DeviceRecord record) {
        final String[] labels = { "High value: every 2 minutes", "High value: every 5 minutes",
            "Standard: every 30 minutes", "Low power: every 60 minutes", "Event-only" };
        final int[] minutes = { 2, 5, 30, 60, 0 };
        new AlertDialog.Builder(this).setTitle("Tracking frequency")
            .setItems(labels, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, final int selected) {
                    db.updateTracking(record.address, minutes[selected], record.pinColor);
                    sendBroadcast(new Intent(BluetoothTrackingService.ACTION_CONFIG_CHANGED)
								  .setPackage(getPackageName()));
                    reload();
                    Toast.makeText(MainActivity.this, pollingLabel(minutes[selected]) + " saved", Toast.LENGTH_SHORT).show();
                    chooseColor(record, minutes[selected]);
                }
            }).show();
    }

    private String pollingLabel(int minutes) {
        return minutes <= 0 ? "GPS event-only" : "GPS every " + minutes + " min";
    }

    private void chooseColor(final DeviceRecord record, final int minutes) {
        final String[] names = { "Red", "Blue", "Green", "Orange", "Purple", "Teal" };
        final String[] colors = { "#E53935", "#1E88E5", "#43A047", "#FB8C00",
            "#8E24AA", "#00897B" };
        new AlertDialog.Builder(this).setTitle("Map pin color")
            .setItems(names, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int selected) {
                    db.updateTracking(record.address, minutes, colors[selected]);
                    sendBroadcast(new Intent(BluetoothTrackingService.ACTION_CONFIG_CHANGED)
								  .setPackage(getPackageName()));
                    reload();
                }
            }).show();
    }

    private void takePhoto() {
        try { startActivityForResult(new Intent(MediaStore.ACTION_IMAGE_CAPTURE), CAMERA); }
        catch (Exception e) { Toast.makeText(this, "No camera app available",
											 Toast.LENGTH_LONG).show(); }
    }

    protected void onActivityResult(int request, int result, Intent data) {
        super.onActivityResult(request, result, data);
        if (request == CAMERA && result == RESULT_OK && editing != null && data != null) {
            Object value = data.getExtras() == null ? null : data.getExtras().get("data");
            if (value instanceof Bitmap) {
                File dir = new File(getFilesDir(), "item_photos");
                if (!dir.exists()) dir.mkdirs();
                File file = new File(dir, safe(editing.address) + ".jpg");
                try {
                    FileOutputStream output = new FileOutputStream(file);
                    ((Bitmap) value).compress(Bitmap.CompressFormat.JPEG, 92, output);
                    output.close();
                    db.updateProfile(editing.address, null, file.getAbsolutePath(),
									 editing.iconPreset);
                    reload();
                } catch (IOException error) {
                    Toast.makeText(this, "Could not save photo", Toast.LENGTH_LONG).show();
                }
            }
        }
    }

    private String safe(String value) { return value.replace(":", "").replace("/", ""); }

    private void showSettings() {
        boolean exempt = isBatteryOptimizationDisabled();
        String background = exempt ? "Background protection: enabled" : "Don't kill background tracking";
        new AlertDialog.Builder(this).setTitle("Dashboard settings")
            .setItems(new String[] { background, "Disconnect alert notifications",
                "Allow location all the time", "Background running / app settings",
                "Per-item tracking information" }, new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					if (which == 0) requestBackgroundProtection();
					else if (which == 1) openNotificationSettings();
					else if (which == 2) openAppSettings("Open Permissions > Location and choose Allow all the time");
					else if (which == 3) openAppSettings("Allow background activity and notifications for continuous tracking");
					else showTrackingInformation();
				}
			}).setNegativeButton("CLOSE", null).show();
    }

    private void openNotificationSettings() {
        try {
            Intent intent;
            if (Build.VERSION.SDK_INT >= 26) {
                intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
                intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
            } else {
                intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.parse("package:" + getPackageName()));
            }
            startActivity(intent);
        } catch (Exception error) { openAppSettings("Enable notifications for disconnect alerts"); }
    }

    private void openAppSettings(String instruction) {
        Toast.makeText(this, instruction, Toast.LENGTH_LONG).show();
        Intent details = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        details.setData(Uri.parse("package:" + getPackageName()));
        startActivity(details);
    }

    private boolean isBatteryOptimizationDisabled() {
        if (Build.VERSION.SDK_INT < 23) return true;
        try {
            PowerManager manager = (PowerManager)getSystemService(POWER_SERVICE);
            return manager != null && manager.isIgnoringBatteryOptimizations(getPackageName());
        } catch (Exception error) { return false; }
    }

    private void requestBackgroundProtection() {
        if (Build.VERSION.SDK_INT < 23) {
            Toast.makeText(this, "Battery optimization restrictions do not apply on this Android version", Toast.LENGTH_LONG).show();
            return;
        }
        if (isBatteryOptimizationDisabled()) {
            Toast.makeText(this, "Background tracking is already excluded from battery optimization", Toast.LENGTH_LONG).show();
            return;
        }
        try {
            Intent request = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
            request.setData(Uri.parse("package:" + getPackageName()));
            startActivity(request);
        } catch (Exception directError) {
            try { startActivity(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)); }
            catch (Exception settingsError) {
                Intent details = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                details.setData(Uri.parse("package:" + getPackageName()));
                startActivity(details);
            }
        }
    }

    private void showTrackingInformation() {
        new AlertDialog.Builder(this).setTitle("Per-item tracking")
            .setMessage("Tracking frequency is configured separately for each item. Use EDIT to set its interval, Track while broadcasting, trail visibility, and map pin color.")
            .setPositiveButton("OK", null).show();
    }

    public void onRequestPermissionsResult(int request, String[] permissions, int[] results) {
        if (request == PERMISSIONS) { startTracker(); reload(); }
    }

    private void requestNeededPermissions() {
        if (Build.VERSION.SDK_INT < 23) return;
        java.util.ArrayList<String> permissions = new java.util.ArrayList<String>();
        addPermission(permissions, "android.permission.ACCESS_FINE_LOCATION");
        addPermission(permissions, "android.permission.CAMERA");
        if (Build.VERSION.SDK_INT >= 31) {
            addPermission(permissions, "android.permission.BLUETOOTH_CONNECT");
            addPermission(permissions, "android.permission.BLUETOOTH_SCAN");
        }
        if (Build.VERSION.SDK_INT >= 33)
            addPermission(permissions, "android.permission.POST_NOTIFICATIONS");
        if (permissions.size() > 0) {
            try {
                Method method = Activity.class.getMethod("requestPermissions",
														 String[].class, Integer.TYPE);
                method.invoke(this, new Object[] {
								  permissions.toArray(new String[permissions.size()]),
								  Integer.valueOf(PERMISSIONS) });
            } catch (Exception ignored) { }
        }
    }

    private void addPermission(java.util.List<String> list, String permission) {
        if (getPackageManager().checkPermission(permission, getPackageName()) !=
            PackageManager.PERMISSION_GRANTED) list.add(permission);
    }

    private void reload() {
        if (adapter == null) return;
        adapter.setRows(DeviceSort.sorted(db.tracked(), reverseSort));
    }

    /*
     * Stale items stay pinned to the bottom in both directions, so the
     * label only describes how the recently seen items are ordered.
     */
    private void updateSortButton() {
        android.widget.Button button =
            (android.widget.Button) findViewById(R.id.sort_button);
        if (button == null) return;
        button.setText(reverseSort ? "Oldest first" : "Newest first");
    }

    private void startTracker() {
        Intent intent = new Intent(this, BluetoothTrackingService.class);
        if (Build.VERSION.SDK_INT >= 26) {
            try {
                Context.class.getMethod("startForegroundService", Intent.class)
                    .invoke(this, intent);
                return;
            } catch (Exception ignored) { }
        }
        startService(intent);
    }
}

